【问题标题】:SQL Union on the Join subsets连接子集上的 SQL 联合
【发布时间】:2017-12-07 22:03:29
【问题描述】:

我有一个特定的要求,后端应该从表中发送符合特定条件的记录。有一个版本表,基本上包含 [id, ..., modstamp, 已删除] 要求是获取时间戳 t1 和 t2 之间匹配的记录。 我想我可以解释一下sql脚本:

create table test (id varchar(10), cv_modstamp DATETIMEOFFSET, sf_deleteddate DATETIMEOFFSET);
insert into test values ('aaa', '2017-10-27 18:45:44 +00:00', null);
insert into test values ('bbb', '2017-10-27 18:45:44 +00:00', NULL);
insert into test values ('eee', '2017-10-27 18:45:44 +00:00', NULL);
insert into test values ('aaa', '2017-10-28 22:45:44 +00:00', NULL);
insert into test values ('bbb', '2017-10-28 22:45:44 +00:00', '2017-10-28 22:45:44 +00:00');
insert into test values ('ccc', '2017-10-28 22:45:44 +00:00', NULL);
insert into test values ('ddd', '2017-10-28 22:45:44 +00:00', '2017-10-28 22:45:44 +00:00');
select * from test;


select D1.*, D2.* from
(
select A.* 
FROM TEST A, (SELECT id AS T_ALIAS_ID, MAX(CV_Modstamp) AS T_ALIAS_lastModTime
                  FROM TEST
                  WHERE CV_Modstamp <= '2017-11-14 18:45:44 +00:00'
                  GROUP BY id) T
WHERE A.id = T.T_ALIAS_ID
 AND   A.CV_Modstamp = T.T_ALIAS_lastModTime
 AND   A.SF_Deleteddate IS NULL
) D1
INNER JOIN
 (
    select A.* 
    FROM TEST A, (SELECT id AS T_ALIAS_ID, MAX(CV_Modstamp) AS T_ALIAS_lastModTime
                  FROM TEST
                  WHERE CV_Modstamp <= '2017-10-27 18:45:44 +00:00'
                  GROUP BY id) T
    WHERE A.id = T.T_ALIAS_ID
    AND   A.CV_Modstamp = T.T_ALIAS_lastModTime
    AND   A.SF_Deleteddate IS NULL
 )
D2 ON D1.ID = D2.ID

这给出了响应:

aaa 2017-10-28 22:45:44 +00:00      aaa 2017-10-27 18:45:44 +00:00  
eee 2017-10-27 18:45:44 +00:00      eee 2017-10-27 18:45:44 +00:00  

但是需要的是:

aaa 2017-10-28 22:45:44 +00:00      
aaa 2017-10-27 18:45:44 +00:00  
eee 2017-10-27 18:45:44 +00:00  
eee 2017-10-27 18:45:44 +00:00  

有没有办法在 union 上显示连接的集合?

编辑: 感谢大家的解决方案。对于 SQL Server,每一个都足够好。我希望在单个查询中实现这一点。也可能将类似的查询移植到其他数据库风格。

编辑2: 如果必须选择整个列集怎么办?

这可以通过 CTE 查询来实现

with subset1 as 
(
    select A.* FROM TEST A,
    (
        SELECT
            id AS T_ALIAS_ID,
            MAX( CV_Modstamp ) AS T_ALIAS_lastModTime
        FROM
            TEST
        WHERE
            CV_Modstamp <= '2017-10-27 18:45:44 +00:00'
        GROUP BY
            id
    ) T
    WHERE
        A.id = T.T_ALIAS_ID
        AND A.CV_Modstamp = T.T_ALIAS_lastModTime
        AND A.SF_Deleteddate IS NULL
)
, subset2 as 
(
    select A.* FROM TEST A,
    (
        SELECT
            id AS T_ALIAS_ID,
            MAX( CV_Modstamp ) AS T_ALIAS_lastModTime
        FROM
            TEST
        WHERE
            CV_Modstamp > '2017-10-27 18:45:44 +00:00'
            AND CV_Modstamp <= '2017-11-14 18:45:44 +00:00'
        GROUP BY
            id
    ) T
    WHERE
        A.id = T.T_ALIAS_ID
        AND A.CV_Modstamp = T.T_ALIAS_lastModTime
        AND A.SF_Deleteddate IS NULL
)

select subset1.* from subset1, subset2 where subset1.id = subset2.id
union all 
select subset2.* from subset1, subset2 where subset1.id = subset2.id
order by id, cv_modstamp;

【问题讨论】:

  • 为什么要复制eee
  • 这是一种可能性:rextester.com/BUJY49274
  • @gh9 感谢您指出这一点。将第一组 where 子句更新为 WHERE CV_Modstamp &gt; '2017-10-27 18:45:44 +00:00' AND CV_Modstamp &lt;= '2017-11-14 18:45:44 +00:00'

标签: sql sql-server join union


【解决方案1】:

这是使用现有代码并将其包装在 CTE 中的一种非常简单的方法。我使用了一个表变量,因此我可以在本地将其作为原型运行而无需使用 drop 语句,否则除了 CTE 包装器之外,代码都是你的。

DECLARE @test AS TABLE
  (
       [id]               VARCHAR(10)
       , [cv_modstamp]    DATETIMEOFFSET
       , [sf_deleteddate] DATETIMEOFFSET
  );

INSERT INTO @test
VALUES      ('aaa','2017-10-27 18:45:44 +00:00',NULL),
            ('bbb','2017-10-27 18:45:44 +00:00',NULL),
            ('eee','2017-10-27 18:45:44 +00:00',NULL),
            ('aaa','2017-10-28 22:45:44 +00:00',NULL),
            ('bbb','2017-10-28 22:45:44 +00:00','2017-10-28 22:45:44 +00:00'),
            ('ccc','2017-10-28 22:45:44 +00:00',NULL),
            ('ddd','2017-10-28 22:45:44 +00:00','2017-10-28 22:45:44 +00:00');

SELECT *
FROM   @test;

WITH [builder]
     AS (SELECT [D1].[id]            AS [d1_id]
                , [D1].[cv_modstamp] AS [d1_cv_modstamp]
                , [D2].[id]          AS [d2_id]
                , [D2].[cv_modstamp] AS [d2_cv_modstamp]
         FROM   (SELECT [A].*
                 FROM   @test [A]
                        , (SELECT [id]                 AS [T_ALIAS_ID]
                                  , MAX([CV_Modstamp]) AS [T_ALIAS_lastModTime]
                           FROM   @test
                           WHERE  [CV_Modstamp] <= '2017-11-14 18:45:44 +00:00'
                           GROUP  BY [id]) [T]
                 WHERE  [A].[id] = [T].[T_ALIAS_ID]
                        AND [A].[CV_Modstamp] = [T].[T_ALIAS_lastModTime]
                        AND [A].[SF_Deleteddate] IS NULL) [D1]
                INNER JOIN (SELECT [A].*
                            FROM   @test [A]
                                   , (SELECT [id]                 AS [T_ALIAS_ID]
                                             , MAX([CV_Modstamp]) AS [T_ALIAS_lastModTime]
                                      FROM   @test
                                      WHERE  [CV_Modstamp] <= '2017-10-27 18:45:44 +00:00'
                                      GROUP  BY [id]) [T]
                            WHERE  [A].[id] = [T].[T_ALIAS_ID]
                                   AND [A].[CV_Modstamp] = [T].[T_ALIAS_lastModTime]
                                   AND [A].[SF_Deleteddate] IS NULL) [D2]
                        ON [D1].[ID] = [D2].[ID]) SELECT [d1_id]
       , [d1_cv_modstamp]
FROM   [builder]
UNION ALL
SELECT [d2_id]
       , [d2_cv_modstamp]
FROM   [builder]; 

【讨论】:

  • 为了概括整行的选择,(select *),这可以在同一张表上不运行另一个连接的情况下完成吗?
【解决方案2】:

要强制将一个源行输出到 2 个输出行,您只需使用一个很小的 ​​cross join 和一个 case expression

select 
       case when cj.n = 1 then d1.id          else d2.id end as id
     , case when cj.n = 1 then d1.CV_Modstamp else d2.CV_Modstamp end as CV_Modstamp
from (
     select A.* 
     FROM TEST A
     INNER JOIN (SELECT id AS T_ALIAS_ID, MAX(CV_Modstamp) AS T_ALIAS_lastModTime
                       FROM TEST
                       WHERE CV_Modstamp <= '2017-11-14 18:45:44 +00:00'
                       GROUP BY id) T
             ON A.id = T.T_ALIAS_ID  AND   A.CV_Modstamp = T.T_ALIAS_lastModTime
     WHERE  A.SF_Deleteddate IS NULL
     ) D1
INNER JOIN (
    select A.* 
    FROM TEST A
    INNER JOIN (SELECT id AS T_ALIAS_ID, MAX(CV_Modstamp) AS T_ALIAS_lastModTime
                  FROM TEST
                  WHERE CV_Modstamp <= '2017-10-27 18:45:44 +00:00'
                  GROUP BY id) T
                ON A.id = T.T_ALIAS_ID AND   A.CV_Modstamp = T.T_ALIAS_lastModTime
    WHERE   A.SF_Deleteddate IS NULL
    ) D2 ON D1.ID = D2.ID
CROSS JOIN (select 1 n union all select 2) cj

dbfiddle demo here

【讨论】:

  • 可以选择推广到这里的所有列,即像 d1.* 或 d2.*?
  • 没有。你不能在这里使用星号。在任何情况下,您都不应该在任何生产查询中使用星号。
【解决方案3】:

我重用了您的查询并将其存储在表temp1 中,使用该表完成了 UNION ALL。

select * into temp1 from (
select D1.*, D2.* from
(
select A.id as A_id, A.cv_modstamp as A_cv_modstamp,  A.sf_deleteddate as A_sf_deleteddate
FROM TEST A, (SELECT id AS T_ALIAS_ID, MAX(CV_Modstamp) AS T_ALIAS_lastModTime
                  FROM TEST
                  WHERE CV_Modstamp <= '2017-11-14 18:45:44 +00:00'
                  GROUP BY id) T
WHERE A.id = T.T_ALIAS_ID
 AND   A.CV_Modstamp = T.T_ALIAS_lastModTime
 AND   A.SF_Deleteddate IS NULL
) D1
INNER JOIN
 (
    select B.id as B_id, B.cv_modstamp as B_cv_modstamp,  B.sf_deleteddate as B_sf_deleteddate
    FROM TEST B, (SELECT id AS T_ALIAS_ID, MAX(CV_Modstamp) AS T_ALIAS_lastModTime
                  FROM TEST
                  WHERE CV_Modstamp <= '2017-10-27 18:45:44 +00:00'
                  GROUP BY id) T
    WHERE B.id = T.T_ALIAS_ID
    AND   B.CV_Modstamp = T.T_ALIAS_lastModTime
    AND   B.SF_Deleteddate IS NULL
 )
D2 ON D1.A_ID = D2.B_ID ) as tt;

select * from temp1;


select A_id, A_cv_modstamp from temp1
UNION ALL
select B_id, B_cv_modstamp from temp1
order by A_id;

【讨论】:

  • 这是一种可能性,需要对多个查询进行一些清理。不过谢谢!
【解决方案4】:

请使用这个:

select 
     id = col.value('(id)[1]', 'nvarchar(256)')
    ,cv_modstamp = col.value('(cv_modstamp)[1]', 'datetimeoffset(7)')
    ,sf_deleteddate = col.value('(sf_deleteddate)[1]', 'datetimeoffset(7)')
from

( select [myData] = convert(xml, (

    select 
         [rec/id] = D1.id, [rec/cv_modstamp] = d1.cv_modstamp, [rec/sf_deleteddate] = d1.sf_deleteddate
        ,[data] = null
        ,[rec/id] = D2.id, [rec/cv_modstamp] = d2.cv_modstamp, [rec/sf_deleteddate] = d2.sf_deleteddate
    from
    (
    select A.* 
    FROM TEST A, (SELECT id AS T_ALIAS_ID, MAX(CV_Modstamp) AS T_ALIAS_lastModTime
                      FROM TEST
                      WHERE CV_Modstamp <= '2017-11-14 18:45:44 +00:00'
                      GROUP BY id) T
    WHERE A.id = T.T_ALIAS_ID
     AND   A.CV_Modstamp = T.T_ALIAS_lastModTime
     AND   A.SF_Deleteddate IS NULL
    ) D1
    INNER JOIN
     (
        select A.* 
        FROM TEST A, (SELECT id AS T_ALIAS_ID, MAX(CV_Modstamp) AS T_ALIAS_lastModTime
                      FROM TEST
                      WHERE CV_Modstamp <= '2017-10-27 18:45:44 +00:00'
                      GROUP BY id) T
        WHERE A.id = T.T_ALIAS_ID
        AND   A.CV_Modstamp = T.T_ALIAS_lastModTime
        AND   A.SF_Deleteddate IS NULL
     )
    D2 ON D1.ID = D2.ID
    FOR XML PATH('data')
))) as t
cross apply t.myData.nodes('data/rec') as tab(col);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-12-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-24
    • 2021-09-23
    • 1970-01-01
    • 2020-06-14
    相关资源
    最近更新 更多