在SQL Server中相交

时间:2014-09-18 20:31:09

标签: sql sql-server 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将是使用所有列构建的(表之间的链接是所有列,基于它们的位置)。

3 个答案:

答案 0 :(得分:5)

在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;

你现在可能至少看到“更为复杂”的含义。

答案 1 :(得分:0)

SELECT
  COLUMN1
FROM B
WHERE B.COLUMN1 IN
  (SELECT COLUMN1
   FROM A)

答案 2 :(得分:0)

SELECT * FROM TableB 
WHERE EXISTS (SELECT 1
              FROM TableA 
              WHERE ColumnName = TableB.ColumnName)