我从外部来源收到原始数据文件,需要对它们进行分析。我将文件加载到表格和表格中将字段设置为varchars,然后运行一个复杂的SQL脚本,进行一些自动分析。我一直试图解决的一个问题是:如何判断一列数据是否与同一个表中的一个或多个其他列重复?
我的目标是为每一列提供一个散列,校验和或类似的东西,按照它们进入的顺序查看每行中列的值。我有动态SQL,它根据INFORMATION_SCHEMA.COLUMNS中列出的字段循环遍历每个字段(不同的表将具有可变数量的列),因此不关心如何完成该部分。
我整天都在研究这个问题,但似乎找不到任何合理的方法来挖掘每一行的每一行。 Google& StackOverflow搜索返回如何对数据行执行各种操作,但我找不到有关如何在字段上垂直执行相同操作的内容。
所以,我考虑了两种可能性&遇到2个障碍:
第二个看起来很有希望但是没有像我希望的那样起作用......
declare @t1 table
(col_1 varchar(5)
, col_2 varchar(5)
, col_3 varchar(5));
insert into @t1
values ('ABC', 'ABC', 'ABC')
, ('ABC', 'ABC', 'BCD')
, ('BCD', 'BCD', NULL)
, (NULL, NULL, 'ABC');
select * from @t1;
select cs_1 = CHECKSUM(col_1)
, cs_2 = CHECKSUM(col_2)
, cs_3 = CHECKSUM(col_3)
from @t1;
select csa_1 = CHECKSUM_AGG(CHECKSUM([col_1]))
, csa_2 = CHECKSUM_AGG(CHECKSUM([col_2]))
, csa_3 = CHECKSUM_AGG(CHECKSUM([col_3]))
from @t1;
在最后一个结果集中,所有3列都返回相同的值:2147449198.
期望的结果:我的目标是让一些代码在csa_1和csa_2带回相同的值,而csa_3带回一个不同的值,表明它是它自己的唯一集合。
答案 0 :(得分:1)
新解决方案
编辑:基于一些新信息,即可能有超过200列,我的建议是计算每列的哈希值,但是在ETL工具中执行它。
实质上,通过转换来提供数据缓冲区,该转换计算先前计算的哈希与当前列值连接的加密哈希。当您到达流的末尾时,您将为每列提供连续生成的哈希值,这些哈希值是每个列的内容和顺序的代理。
然后,您可以几乎立即将每个人与所有其他人进行比较,而不是运行20,000次表扫描。
旧解决方案
试试这个。基本上,您需要这样的查询来分析每个列与其他列。实际上没有可行的基于散列的解决方案。只需按插入顺序(某种行序列号)比较每个集合。如果你有一个计算上可行的方法,可以在摄取过程中生成这个数字,或者在检索过程中对其进行投影。
注意:我在这里使用了NULL,将它作为空字符串进行比较。
declare @t1 table
(
rownum int identity(1,1)
, col_1 varchar(5)
, col_2 varchar(5)
, col_3 varchar(5));
insert into @t1
values ('ABC', 'ABC', 'ABC')
, ('ABC', 'ABC', 'BCD')
, ('BCD', 'BCD', NULL)
, (NULL, NULL, 'ABC');
with col_1_sets as
(
select
t1.rownum as col_1_rownum
, CASE WHEN t2.rownum IS NULL THEN 1 ELSE 0 END AS col_2_miss
, CASE WHEN t3.rownum IS NULL THEN 1 ELSE 0 END AS col_3_miss
from
@t1 as t1
left join @t1 as t2 on
t1.rownum = t2.rownum
AND isnull(t1.col_1, '') = isnull(t2.col_2, '')
left join @t1 as t3 on
t1.rownum = t3.rownum
AND isnull(t1.col_1, '') = isnull(t2.col_3, '')
),
col_1_misses as
(
select
SUM(col_2_miss) as col_2_misses
, SUM(col_3_miss) as col_3_misses
from
col_1_sets
)
select
'col_1' as column_name
, CASE WHEN col_2_misses = 0 THEN 1 ELSE 0 END AS is_col_2_match
, CASE WHEN col_3_misses = 0 THEN 1 ELSE 0 END AS is_col_3_match
from
col_1_misses
结果:
+-------------+----------------+----------------+
| column_name | is_col_2_match | is_col_3_match |
+-------------+----------------+----------------+
| col_1 | 1 | 0 |
+-------------+----------------+----------------+
答案 1 :(得分:1)
您可以通过这种方式比较每个列组合,而不是使用哈希:
select case when count(case when column1 = column2 then 1 else null end) = count(1) then 1 else 0 end Column1EqualsColumn2
, case when count(case when column1 = column3 then 1 else null end) = count(1) then 1 else 0 end Column1EqualsColumn3
, case when count(case when column1 = column4 then 1 else null end) = count(1) then 1 else 0 end Column1EqualsColumn4
, case when count(case when column1 = column5 then 1 else null end) = count(1) then 1 else 0 end Column1EqualsColumn5
, case when count(case when column2 = column3 then 1 else null end) = count(1) then 1 else 0 end Column2EqualsColumn3
, case when count(case when column2 = column4 then 1 else null end) = count(1) then 1 else 0 end Column2EqualsColumn4
, case when count(case when column2 = column5 then 1 else null end) = count(1) then 1 else 0 end Column2EqualsColumn5
, case when count(case when column3 = column4 then 1 else null end) = count(1) then 1 else 0 end Column3EqualsColumn4
, case when count(case when column3 = column5 then 1 else null end) = count(1) then 1 else 0 end Column3EqualsColumn5
, case when count(case when column4 = column5 then 1 else null end) = count(1) then 1 else 0 end Column4EqualsColumn5
from myData a
以下是设置代码:
create table myData
(
id integer not null identity(1,1)
, column1 nvarchar (32)
, column2 nvarchar (32)
, column3 nvarchar (32)
, column4 nvarchar (32)
, column5 nvarchar (32)
)
insert myData (column1, column2, column3, column4, column5)
values ('hello', 'hello', 'no', 'match', 'match')
,('world', 'world', 'world', 'world', 'world')
,('repeat', 'repeat', 'repeat', 'repeat', 'repeat')
,('me', 'me', 'me', 'me', 'me')
这是必须的SQL Fiddle。
另外,为了节省您必须在此处编写此代码以生成上述代码。此版本还将包含处理两个列的值为空的方案的逻辑:
declare @tableName sysname = 'myData'
, @sql nvarchar(max)
;with cte as (
select name, row_number() over (order by column_id) r
from sys.columns
where object_id = object_id(@tableName, 'U') --filter on our table
and name not in ('id') --only process for the columns we're interested in
)
select @sql = coalesce(@sql + char(10) + ', ', 'select') + ' case when count(case when ' + quotename(a.name) + ' = ' + quotename(b.name) + ' or (' + quotename(a.name) + ' is null and ' + quotename(b.name) + ' is null) then 1 else null end) = count(1) then 1 else 0 end ' + quotename(a.name + '_' + b.name)
from cte a
inner join cte b
on b.r > a.r
order by a.r, b.r
set @sql = @sql + char(10) + 'from ' + quotename(@tableName)
print @sql
注意:这并不是说你应该把它作为动态SQL来运行;相反,您可以使用它来生成代码(除非您需要支持列的数量或名称在运行时可能会有所不同的情况,在这种情况下,您显然需要动态选项)。