【问题标题】:Compare 2 result sets without INTERSECT比较没有 INTERSECT 的 2 个结果集
【发布时间】:2015-05-24 09:17:18
【问题描述】:

我有两张桌子:-

ITEM(ITEM_ID, ITEM_NAME)

STATS(ITEM_ID, STAT_ID, STAT_VALUE)

我想用完全相同的STATS 返回ITEM_IDs,但它在 SQL Server Compact 上(没有 EXCEPTINTERSECT

例如:

STATS:-
1 12 100 
1 13 500
2 12 200
2 14 300
3 12 100
3 13 500
4 12 100

应该返回 1 和 3 的行(相同的统计数据/值 12/100 和 13/500)

没有INTERSECT可以吗?

【问题讨论】:

  • 这些列可以为空吗?
  • 您如何使用INTERSECT 获得该结果? ;)。
  • 如果有一行5 13 500,是否应该将其包含在结果中,还是仅当也有5 12 100 时才包含?
  • 5 13 500 需要包含 5 12 100 (与 1 和 3 完全相同的统计数据)
  • 所以你正在寻找的是“寻找相等的集合”。 ITEMSSTATS 表中有多少行?

标签: sql sql-server-ce


【解决方案1】:

在 ITEM 表中使用内部连接并在条​​件下使用您想在该表上看到的任何内容

【讨论】:

    【解决方案2】:

    我第一次完全错了:-)

    “查找相等集合”的标准方法很难理解,而且对于大型表来说性能通常很差,因为它们涉及将所有内容与其他所有内容进行比较(类似于 CROSS JOIN)。

    AFAIK SQL Server CE 也支持 XML 函数,因此最好的方法是 Vladimir Baranov 在现有答案之一中描述的方法:

    with cte as
     ( -- group concat all rows for one ITEM_ID into one big string
       SELECT distinct ITEM_ID,
         (select '#' + rtrim(STAT_ID) + ',' + rtrim(STAT_VALUE) 
          from STATS as t2 
          where t1.ITEM_ID = t2.ITEM_ID ORDER BY STAT_ID FOR XML PATH('') ) as rowsConcat
       FROM STATS as t1
     ),
    cnts as
     ( -- how many rows exist for that concatenated string?
       select *
         ,count(*) 
          over (partition by rowsConcat) as cnt
       from cte
     ) 
    select ITEM_ID
      ,dense_rank() -- assign the same group number to the duplicates 
       over(order by rowsConcat) as duplicateGroup
    from cnts
    where cnt > 1 -- more than one
    

    fiddle

    【讨论】:

    • 这个想法看起来不错,但是 SQL Server CE 非常有限,它不支持“FOR XML”。但我会继续朝这个方向寻找……谢谢。
    • @Fred:我假设CE类似于Express版本,CE似乎非常有限,没有CTE,没有ROW_NUMBER。搜索sql "equal sets",你会发现一些来自Joe Celko 书中的摘录,答案#5 应该可以用CE,但性能可能很糟糕......
    • @dnoeth,感谢您提及 Joe Celko 的书。我找到了,值得一读。
    【解决方案3】:

    以下是我将如何处理此类任务。

    第 1 步。 具有将多列中的值合并为一列的函数/方法。在您的情况下,我们希望将两个值 STAT_IDSTAT_VALUE 放在一起。在这种情况下,简单地转换为字符串和连接就足够了。所以,一行有两列:

    STAT_ID STAT_VALUE
    12      100 
    

    变成一行一列:

    single_value
    12_100
    

    这个转换的结果应该是这样的表格:

    ITEM_ID single_value
    1       12_100 
    1       13_500
    2       12_200
    2       14_300
    3       12_100
    3       13_500
    4       12_100
    

    第 2 步。 有一些 Aggregate 函数,它接受多行中的值并返回一个值作为长连接字符串。它可以是 CLR 函数、T-SQL 函数或 FOR XML 构造。您可以根据 SQL Server CE 支持的方式选择一种。

    这个转换的结果应该是这样的表格:

    ITEM_ID aggregated_single_value
    1       12_100__13_500
    2       12_200__14_300
    3       12_100__13_500
    4       12_100
    

    第 3 步。 获得此结果后,您可以简单地 GROUP BY aggregated_single_valueCOUNT 有多少 ITEM_IDs 具有完全相同的一组值。然后只返回那些ITEM_IDs 大于 1 的HAVING

    aggregated_single_value  Count
    12_100__13_500           2
    12_200__14_300           1
    12_100                   1
    

    编辑

    总体方法仍然存在 SQL Server CE 的局限性。

    • 它没有 Common-Table-Expressions - 每个步骤都使用显式临时表。
    • 它没有FOR XML、用户定义函数、CLR——“手动”进行聚合。看起来像supports cursors。打开游标 - 以正确的顺序扫描表一次,汇总值并将它们保存到临时表中。
    • 它是否支持像varbinary(max)varchar(max) 这样的大类型?如果是 - 很好,如果不是 - 您将被限制为 varchar(8000)varbinary(8000)。如果 STATS 表中的同一 ITEM_ID 最多有两行(左右),那么 8000 字节就足够了。

    此外,至少有一种简单粗暴的方法来限制游标处理的行数。首先对STATS 表中的每个ITEM_ID 进行简单的行计数,只留下那些具有匹配计数的IDs。换句话说,过滤掉明显的不匹配。此步骤将从您的示例中删除 ID=4

    【讨论】:

    • 感谢您的想法,但 SQL Server CE 似乎不支持直接聚合行的任何东西...
    • 有用户自定义函数吗?基本上,您需要找到一种方法来为您的 SQL Server 版本执行group-concat。顺便问一下,您的服务器的确切版本是什么?
    • 它是 SQL Server CE 4.0,许多“不支持”的特性:technet.microsoft.com/en-us/library/…
    • 我添加了一些关于如何解决 SQL Server CE 限制的注释。
    【解决方案4】:

    我不确定SQL Server Compact 是否支持内部查询,但这个概念应该可行:

    select distinct a.item_id 
           from  stats a 
           where exists(select 1 
                               from stats b 
                               where a.stat_d=b.stat_id 
                                and a.stat_value=b.stat_value)
    

    【讨论】:

    • 谢谢,但这会返回不需要的行,例如 4(12/100 存在但不存在 13/500)
    【解决方案5】:

    INTERSECT
    返回由 INTERSECT 操作数左右两侧的查询返回的任何不同值。

    使用INTERSECT 的替代方法是像这样使用JOIN

    SELECT A.*
    FROM A   -- [A: ID, Name]
    INTERSECT
    SELECT B.*
    FROM B  -- [B: ID, Name]
    

    等于

    SELECT DISTINCT A.*
    FROM A
    JOIN (
        SELECT B.*
        FROM B) B1 ON A.ID = B1.ID AND A.Name = B1.Name
    

    或使用EXISTS

    SELECT DISTINCT A.*
    FROM A
    WHERE EXISTS (
        SELECT 1 
        FROM B WHERE A.ID = B.ID AND A.Name = B.Name)
    

    试试这个查询:

    SELECT DISTINCT s1.STAT_ID, s1.STAT_VALUE
    FROM STATS s1
        JOIN
        STATS s2 ON s1.ITEM_ID <> s2.ITEM_ID 
                AND s1.STAT_ID = s2.STAT_ID 
                AND s1.STAT_VALUE = s2.STAT_VALUE
    

    【讨论】:

    • 与第一个答案相同的问题:这将返回不需要的行(存在一行但不存在其他行)
    • @Fred 如果我知道How do you use INTERSECT for that results?,我可以为您提供更多帮助,这里我向您展示一些不使用INTERSECT 的方法;)。
    • 我已经明确了这一点,因为我通常将 INTERSECT 与其他 SGBD 用于这些类型的查询(ORACLE、Sybase 等...),并且它不在 SQL CE 上。
    • @Fred 好的,例如,请给我您在 Sybase 中的查询;)。
    • 也许 INTERSECT 在这种情况下不起作用:我只是想用 INTERSECT 排除答案 :-)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-26
    相关资源
    最近更新 更多