【问题标题】:How to query for rows containing <Unable to read data> in a column?如何查询列中包含 <无法读取数据> 的行?
【发布时间】:2012-05-23 17:13:09
【问题描述】:

我有一个 SQL 表,其中一些列在 SQL Server 管理器中查看时包含&lt;Unable to read data&gt;。有谁知道如何查询&lt;Unable to read data&gt;?我可以使用update table set column = NULL where key = 'value' 单独修改该列中的数据,但是如何查找这些不良数据是否存在其他行?

【问题讨论】:

  • 哪些数据无法读取?是主键吗?你试过delete from myTable where KeyField='KeyValue'吗?反应如何?
  • 表中有280个字段,共有9行数据。第 9 行的一半字段已正确填写,但在所有剩余字段中均写入 。表中存在字符串、整数和图像字段。我试图在具有 的字段中写入 NULL,但它被禁用(字母很暗)并且不允许这样做,并且该行甚至没有被删除。
  • 我自己也遇到过这样的情况。在 SQL Server Management Studio 中,查看该行的数据时,有一列显示 。如何查询这些行?
  • 根据MSDNPrecision and scale can not always be preserved. For example, the Results pane supports a precision of 27. If data is of a data type with a greater precision, the data may be truncated or may be represented by &lt;Unable to read data&gt;.
  • @Jonathon :有一个名为 ISDATE 的内置函数,它确定输入表达式是否为有效日期。Select * from YourTable where ISDATE(Col)!=1 。这将导致所有日期格式不正确的行。

标签: sql-server database corruption


【解决方案1】:

我建议不要替换数据。这没有什么问题,只是 SSM 无法在编辑面板中正确显示它。根据您的描述,数据库本身的数据非常好。

这个脚本显示了问题:

create table test (id int not null identity(1,1) primary key, 
    large_value numeric(38,0));
go

insert into test (large_value) values (1);
insert into test (large_value) values (12345678901234567890123456789012345678);
insert into test (large_value) values (1234567890123456789012345678901234567);
insert into test (large_value) values (123456789012345678901234567890123456);
insert into test (large_value) values (12345678901234567890123456789012345);
insert into test (large_value) values (1234567890123456789012345678901234);
insert into test (large_value) values (123456789012345678901234567890123);
insert into test (large_value) values (12345678901234567890123456789012);
insert into test (large_value) values (1234567890123456789012345678901);
insert into test (large_value) values (123456789012345678901234567890);
insert into test (large_value) values (12345678901234567890123456789);
insert into test (large_value) values (NULL);
go

select * from test;
go

SELECT 可以正常工作,但在对象资源管理器中显示 Edit Top 200 Rows 将不会:

这个问题有一个Connect Item。 SSMS 2012 仍然存在同样的问题。

如果我们查看Numeric and Decimal 的详细信息,我们会发现问题发生在一个奇怪的边界上,精度为 29,实际上 不是 SQL Server 边界(精度为 28):

Precision   Storage bytes
1 - 9   5
10-19   9
20-28   13
29-38   17

如果我们检查 .Net(SSMS 是托管应用程序)decimal precision table,我们可以很快发现问题的症结所在:精度为 28-29 位有效数字。所以.Net decimal 类型无法映射高精度(>29)SQL Server numeric/decimal 类型。

这不仅会影响 SSMS 显示,还会影响您的应用程序。像 SSIS 这样的专业应用程序将使用像 DT_NUMERIC 这样的高精度表示:

DT_NUMERIC 具有固定精度和小数位数的精确数值。 此数据类型是一个 16 字节 无符号整数,带有一个单独的符号,a 范围为 0 - 38,最大精度为 38。

现在回到您的问题:您只需查看值即可发现无效条目。知道 C# 表示范围可以容纳近似值(-7.9 x 1028 到 7.9 x 1028)/(100 到 28 )`(范围取决于比例)您可以在每列上搜索范围之外的值(要搜索的实际值将取决于列比例)。但这引出了一个问题“用什么替换数据?”。

我建议改为使用专用工具进行导入导出,即能够处理高精度数值的工具。 SSIS 是显而易见的候选人。但即使是谦虚的bcp.exe 也符合要求。

顺便说一句,如果您的值实际上不正确(即真正的损坏),那么我建议您运行 DBCC CHECKTABLE (...) WITH DATA_PURITY:

DATA_PURITY

导致 DBCC CHECKDB 检查数据库中是否存在无效或超出范围的列值。例如,DBCC CHECKDB 检测 日期和时间值大于或小于的列 datetime 数据类型的可接受范围;或小数或 具有小数位数或精度值的近似数值数据类型列 无效。

对于在 SQL Server 2005 及更高版本中创建的数据库,默认启用列值完整性检查,并且不需要 DATA_PURITY 选项。对于从早期版本升级的数据库 SQL Server,默认情况下不启用列值检查,直到 DBCC CHECKDB WITH DATA_PURITY 已在数据库上无错误地运行。 此后,DBCC CHECKDB 默认检查列值完整性。

问:datetime 列怎么会出现这个问题?

use tempdb;
go

create table test(d datetime)

insert into test (d) values (getdate())

select %%physloc%%, * from test;

-- Row is on page  0x9100000001000000

dbcc traceon(3604,-1);

dbcc page(2,1,145,3);

Memory Dump @0x000000003FA1A060
0000000000000000:   10000c00 75f9ff00 6aa00000 010000             ....uùÿ.j .....
Slot 0 Column 1 Offset 0x4 Length 8 Length (physical) 8

dbcc writepage(2,1,145, 100, 8, 0xFFFFFFFFFFFFFFFF)

dbcc checktable('test') with data_purity;

消息 2570,级别 16,状态 3,第 2 行页面 (1:145),对象 ID 中的插槽 0 837578022,索引ID 0,分区ID 2882303763115671552,分配单元ID 2882​​303763120062464(键入“行内数据”)。列“d”值超出 数据类型“日期时间”的范围。将列更新为合法值。

【讨论】:

  • datetime 列怎么会出现这个问题?
  • @MartinSmith 看到我的更新。孩子们,不要在家里这样做 :) 不要随意使用 DBCC WRITEPAGE 破坏您的数据库...
  • 优秀的答案,尤其是通过破坏日期时间数据的再现!请注意,使用dbcc checktable 发现的错误可以通过运行dbcc checktable('test') repair_allow_data_loss 来删除。
  • @Jonathon:我真的认为allow_data_loss 是万不得已的万不得已……我宁愿先尝试从良好的备份中恢复数据。
【解决方案2】:

如上所述,这些错误通常发生在未保留精度和比例时。如果您对 SSIS 感到满意,那么您可以获取那些损坏的行。采用 Martin Smith 创建的值

CREATE TABLE T(ID int ,C DECIMAL(38,0));
INSERT INTO T VALUES(1,9999999999999999999999999999999999999) 

上表重现了错误。这里第一列代表主键。我插入了大约 1000 行,其中很少有损坏的值。下面是SSIS包设计

在数据转换中,我取了有错误的列 C 并尝试将其转换为 Decimal(38,0)。由于会发生转换或截断错误,因此我将错误行重定向到基本上是 OLEDB 命令更新表并将列设置为 NULL

Update T
Set C=NULL
where ID=?

C 和 ID 的值将被定向到 oledb 命令。如果没有错误,那么我只是插入到表中(实际上不需要这样做)。如果您有主键,这将起作用表中的列。

如果日期时间列有任何错误,可以编写一个sql查询来验证日期时间值的格式。请通过MSDN link获取有效的日期时间值

   Select * from YourTable where ISDATE(Col)!=1

【讨论】:

  • 要明确,9999999999999999999999999999999999999 没有任何损坏,SSMS 显示时间过长。
  • 我实际上并不是指损坏的数据,但它只是 sql server 支持 27 的精度,因此插入上面的值可能会导致截断
  • 在运行select * from table where ISDATE(col) != 1 时,我收到An error occurred while executing batch. Error message is: SqlDateTime overflow. Must be between 1/1/1753 12:00:00 AM and 12/31/9999 11:59:59 PM. 看起来该查询不适用于无效数据。
  • @Jonathan:我希望这就是你要找的stackoverflow.com/questions/5124187/…
【解决方案3】:

我认为您可以使用游标获取数据。请使用光标查询重试,例如以下查询:

DECLARE VerifyCursor CURSOR FOR
SELECT *
FROM MyTable
WHILE 1=1 BEGIN
    BEGIN Try
        FETCH FIRST FROM VerifyCursor INTO @Column1, @Column2, ...

        INSERT INTO @MyTable2(Column1, Column2,...)
        VALUES (@Column1, @Column2, ...)
    END TRY
    BEGIN CATCH
    END CATCH
    IF (@@FETCH_STATUS<>0) BREAK
End
OPEN VerifyCursor
CLOSE VerifyCursor
DEALLOCATE VerifyCursor

【讨论】:

    【解决方案4】:

    用更新替换坏数据很简单:

    UPDATE table SET column = NULL WHERE key_column = 'Some value'
    

    【讨论】:

    • 我认为问题是在列中找到坏行。如果有几行数据不好..!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-21
    • 1970-01-01
    • 2012-03-07
    • 2017-01-21
    • 2018-03-09
    相关资源
    最近更新 更多