sql - SQL Server 中的相交

标签 sql sql-server intersect

有没有一种方法可以使用 intersect 而无需仅选择不同的值?类似于INTERSECT ALL

例如,考虑表 A 和 B

A --> 1, 1, 1, 2, 3, 4

B --> 1, 1, 2

会导致

Result --> 1, 1, 2

编辑

我认为这个link很好地解释了我想要什么。这个other link理解这个问题也很有趣。或this other link更好地解释事件。

编辑2

假设表格:

表A

╔════════╦════╦═══╦════╦════╗
║   A    ║ B  ║ C ║ D  ║ E  ║
╠════════╬════╬═══╬════╬════╣
║ Car    ║ 10 ║ 1 ║ OK ║ -1 ║
║ Car    ║ 10 ║ 1 ║ OK ║ -1 ║
║ Car    ║ 10 ║ 1 ║ OK ║ -1 ║
║ House  ║ 10 ║ 1 ║ NO ║ -5 ║
║ Monkey ║ 15 ║ 1 ║ OK ║ -1 ║
║ Dog    ║  3 ║ 1 ║ OK ║ -1 ║
╚════════╩════╩═══╩════╩════╝

表B

╔═════╦════╦═══╦════╦════╗
║  A  ║ B  ║ C ║ D  ║ E  ║
╠═════╬════╬═══╬════╬════╣
║ Car ║ 10 ║ 1 ║ OK ║ -1 ║
║ Car ║ 10 ║ 1 ║ OK ║ -1 ║
║ Car ║ 15 ║ 1 ║ OK ║ -1 ║
║ Dog ║  3 ║ 1 ║ OK ║ -1 ║
╚═════╩════╩═══╩════╩════╝

相交(select * from A INTERSECT select * from B)的答案是:

╔═════╦════╦═══╦════╦════╗
║  A  ║ B  ║ C ║ D  ║ E  ║
╠═════╬════╬═══╬════╬════╣
║ Car ║ 10 ║ 1 ║ OK ║ -1 ║
║ Dog ║  3 ║ 1 ║ OK ║ -1 ║
╚═════╩════╩═══╩════╩════╝

因为它只需要不同的值。我想要的是采用常见的行,就像:

╔═════╦════╦═══╦════╦════╗
║  A  ║ B  ║ C ║ D  ║ E  ║
╠═════╬════╬═══╬════╬════╣
║ Car ║ 10 ║ 1 ║ OK ║ -1 ║
║ Car ║ 10 ║ 1 ║ OK ║ -1 ║
║ Dog ║  3 ║ 1 ║ OK ║ -1 ║
╚═════╩════╩═══╩════╩════╝

观察我不需要知道我必须链接什么(连接是位置性的,就像INTERSECT)。 ID 将是使用所有列构建的内容(表之间的链接是所有列,基于它们的位置)。

最佳答案

在 SQL Server 中,INTERSECT 仅适用于不同的行。如果您希望它区分重复的行,则需要使行不同。我能想到的唯一方法是添加另一列并用每个重复项的唯一值填充它,但以这种方式生成的行可以在不同的表中匹配。

然而,问题是到目前为止还没有通用的语法。例如,您可以使用 ROW_NUMBER() 枚举每个重复项,但您必须为每种情况单独写出 PARTITION BY 子句:没有 PARTITION BY *,至少在 SQL Server 中没有.

无论如何,为了便于说明,ROW_NUMBER 方法如下所示:

SELECT
  A, B, C, D, E,
  ROW_NUMBER() OVER (PARTITION BY A, B, C, D, E ORDER BY (SELECT 1))
FROM
  dbo.A

INTERSECT

SELECT
  A, B, C, D, E,
  ROW_NUMBER() OVER (PARTITION BY A, B, C, D, E ORDER BY (SELECT 1))
FROM
  dbo.B
;

如上所述,查询还将在输出中返回一个额外的列,即行号列。如果您想抑制它,则需要使查询更复杂:

SELECT
  A, B, C, D, E
FROM
  (
    SELECT
      A, B, C, D, E,
      rn = ROW_NUMBER() OVER (PARTITION BY A, B, C, D, E ORDER BY (SELECT 1))
    FROM
      dbo.A

    INTERSECT

    SELECT
      A, B, C, D, E,
      rn = ROW_NUMBER() OVER (PARTITION BY A, B, C, D, E ORDER BY (SELECT 1))
    FROM
      dbo.B
  ) AS s
;

为了澄清,当我上面说没有通用语法时,我的意思是如果不借助动态 SQL 就无法做到这一点。使用动态 SQL,很多事情都是可能的,但这样的解决方案会更加复杂,而且在我看来,可维护性要差得多。

再次,为了说明这一点,这是一个如何使用动态 SQL 解决该问题的示例:

DECLARE
  @table1 sysname,
  @table2 sysname,
  @columns nvarchar(max),
  @sql nvarchar(max)
;

SET @table1 = 'dbo.A';
SET @table2 = 'dbo.B';

-- collecting the columns from one table only,
-- assuming the structures of both tables are identical
-- if the structures differ, declare and populate
-- @columns1 and @columns2 separately
SET @columns = STUFF(
  (
    SELECT
      N', ' + QUOTENAME(name)
    FROM
      sys.columns
    WHERE
      object_id = OBJECT_ID(@table1)
    FOR XML
      PATH (''), TYPE
  ).value('text()[1]', 'nvarchar(max)'),
  1,
  2,
  ''
);

SET @sql =
N'SELECT ' + @columns + N'
FROM
  (
    SELECT
      ' + @columns + N',
      ROW_NUMBER() OVER (PARTITION BY ' + @columns + N' ORDER BY (SELECT 1))
    FROM
      ' + @table1 + N'

    INTERSECT

    SELECT
      ' + @columns + N',
      ROW_NUMBER() OVER (PARTITION BY ' + @columns + N' ORDER BY (SELECT 1))
    FROM
      ' + @table2 + N'
  ) AS s
';

EXECUTE sp_executesql @sql;

您现在可能至少可以明白我所说的“更加复杂”是什么意思。

关于sql - SQL Server 中的相交,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25921790/

相关文章:

sql - Postgresql查询仅包含日期的时间戳数据

c# - SqlCommand 只返回一行

vba - Excel VBA 相交

mysql - 间隔日期之间的查询

mysql - 从多个表中获取结果

sql - 数据建模草稿/报价/订单/发票

.net - 根据 SQL 表时间戳安排 .exe 执行

MYSQL 对具有唯一结果的列求和

sql-server - SQL Server Express 与 MS Access