如何将表外连接到查询的子集

时间:2016-02-10 20:35:56

标签: sql

假设您有一个返回如下记录集的查询:

Date      |   Code  | Val 1  | Val 2
1/1/2016       A       1        2
1/1/2016       B       3        4
1/2/2016       A       5        6
1/2/2016       B       7        8
1/2/2016       C       9        10

第二个表包含" Code"的所有可能值。来自子查询:

Code
A
B
C
D

如何将两个集合连接起来,以便在第一个表中为每个日期的第二个表中的每个缺少的代码添加一个记录。在上面的示例中,结果查询应为:

Date      |   Code  | Val 1  | Val 2
1/1/2016       A       1        2
1/1/2016       B       3        4
1/1/2016       C       NULL    NULL
1/1/2016       D       NULL    NULL
1/2/2016       A       5        6
1/2/2016       B       7        8
1/2/2016       C       9        10
1/2/2016       D       NULL     NULL

“代码”列上的外部联接适用于一天,但由于日期未包含在第二个集(或联接)中,因此在应用于跨越多个日期的集时,它不起作用。

4 个答案:

答案 0 :(得分:1)

示例数据:

CREATE TABLE #temp (DateField DATE      ,   Code CHAR  , Val1  INT, Val2 INT)
INSERT INTO #temp
VALUES
('1/1/2016','A','1','2'),
('1/1/2016','B','3','4'),
('1/2/2016','A','5','6'),
('1/2/2016','B','7','8'),
('1/2/2016','C','9','10')

CREATE TABLE #Code (Code CHAR )
INSERT INTO #Code
VALUES('A'),
('B'),
('C'),
('D')

查询:

  1. 获取#temp中所有不同日期的笛卡尔积和#Code中的所有代码并放入公用表格CTE中
  2. LEFT OUTER JOIN #temp with Code Table Expression CTE on Code 和DateField
  3. 查询:

    WITH CTE AS ( SELECT * 
                  FROM (SELECT DISTINCT DateField 
                        FROM #temp) AS A CROSS JOIN 
                        #Code AS C
                )
    SELECT C.*,t.Val1,t.Val2 
    FROM CTE AS C LEFT OUTER JOIN  
         #temp AS t ON c.Code=t.Code AND c.DateField=t.DateField
    

    结果:

    enter image description here

答案 1 :(得分:1)

您没有说您正在使用哪个数据库;如果您没有可用的CTE,您也可以使用Calendar表。

我假设您的数据是Orders的表格,只是为了给表格命名。

CREATE TABLE Orders
(order_date date
 ,code varchar(1)
 ,val_1 int
 ,val_2 int);

INSERT INTO Orders
    ([order_date], [code], [val_1], [val_2])
VALUES
    ('2016-01-01 00:00:00', 'A', 1, 2),
    ('2016-01-01 00:00:00', 'B', 3, 4),
    ('2016-01-02 00:00:00', 'A', 5, 6),
    ('2016-01-02 00:00:00', 'B', 7, 8),
    ('2016-01-02 00:00:00', 'C', 9, 10);

CREATE TABLE Codes(code CHAR(1));
INSERT INTO Codes(code) VALUES 
('A'),
('B'),
('C'),
('D');

CREATE TABLE Calendar(calendar_date DATE);
INSERT INTO Calendar(calendar_date) VALUES
('1/1/2016'),
('1/2/2016')

然后,您可以加入这些内容以获取每天的所有codes

SELECT calendar_date, T.code, val_1, val_2
FROM 
    (SELECT calendar_date, code FROM Codes CROSS JOIN Calendar) AS T
LEFT JOIN Orders ON T.calendar_date = Orders.order_date
AND T.code = Orders.code
ORDER BY calendar_date, T.code

查询结果

calendar_date   code    val_1   val_2
-------------   ----    -----   -----
"2016-01-01"    "A"     "1"     "2"
"2016-01-01"    "B"     "3"     "4"
"2016-01-01"    "C"     "NULL"  "NULL"
"2016-01-01"    "D"     "NULL"  "NULL"
"2016-01-02"    "A"     "5"     "6"
"2016-01-02"    "B"     "7"     "8"
"2016-01-02"    "C"     "9"     "10"
"2016-01-02"    "D"     "NULL"  "NULL"

日历表的一个优点是,当您希望使用所有日期和代码填充结果时,即使主表中没有匹配的行也是如此。例如,添加另一个日期会产生结果

calendar_date   code    val_1   val_2
-------------   ----    -----   -----
"2016-01-01"    "A"     "1"     "2"
"2016-01-01"    "B"     "3"     "4"
"2016-01-01"    "C"     "NULL"  "NULL"
"2016-01-01"    "D"     "NULL"  "NULL"
"2016-01-02"    "A"     "5"     "6"
"2016-01-02"    "B"     "7"     "8"
"2016-01-02"    "C"     "9"     "10"
"2016-01-02"    "D"     "NULL"  "NULL"
"2016-01-03"    "A"     "NULL"  "NULL"
"2016-01-03"    "B"     "NULL"  "NULL"
"2016-01-03"    "C"     "NULL"  "NULL"
"2016-01-03"    "D"     "NULL"  "NULL"

答案 2 :(得分:1)

使用cross joinleft join

select d.date, c.code, q.val1, q.val2
from (select distinct date from q) d cross join
     codes c left join
     q
     on d.date = q.date and c.code = q.code;

cross join生成所有行。 left join会带来您感兴趣的值。

答案 3 :(得分:0)

尝试以下(SQL服务器)

DECLARE @dataTable TABLE (SampleDate DATE, Code CHAR(1), VALUE1 VARCHAR(10), VALUE2 VARCHAR(10));

INSERT INTO @dataTable (SampleDate, Code, VALUE1, VALUE2)
VALUES
('1/1/2016', 'C', 3, 2),
('1/1/2016', 'A', 3, 2),
('1/2/2016', 'A', 6, 4),
('1/5/2016', 'B', 9, 8);

DECLARE @Codes TABLE (Code CHAR(1));
INSERT INTO @Codes (Code)
VALUES
('A'),
('B'),
('C'),
('D');

 WITH DateList AS (
    SELECT CAST('2016-01-01' AS date) AS [DateKey]
    UNION ALL
    SELECT  DATEADD(DAY,1,[DateKey])
    FROM    DateList   
    WHERE   DATEADD(DAY,1,[DateKey]) <= CAST('2016-12-31' AS date))

SELECT s.DateKey, s.Code, dat.VALUE1, dat.VALUE2

FROM @dataTable dat RIGHT JOIN (
                SELECT  d.DateKey, c.Code
                FROM    @Codes c, DateList d) s 
    ON dat.Code = s.Code AND dat.SampleDate = s.DateKey

ORDER BY s.DateKey, s.Code
OPTION (MAXRECURSION 0)

enter image description here