执行此类查询需要什么样的JOIN?

时间:2012-05-18 08:58:06

标签: sql sql-server sql-server-2008

假设我有三个表:

  • 汽车(CarID)包含10个dstinct汽车排。
  • SafetyTests(TestID,TestName)包含20个不同的安全测试行。
  • TestResults(ID,TestID,CarID,TestValue)包含0到200个安全测试行之间的数字。

在安全测试表中,它包含从Cars表中对10辆汽车进行的每项安全测试的结果。最多有20个安全测试 - 显然是从SafetyTests表的select语句中获得的。然而,10辆汽车中的一些已经进行了所有20次测试,而其他汽车只有5次。

我想生产一个10 x 20矩阵,每辆车显示20个结果(即使他们没有20个安全测试结果)。如果尚未对汽车进行测试,则只显示测试名称,但值为零(或为空)。

我认为这将是SafetyTests表上的SELECT(以获取不同的测试ID列表)和LEFT JOIN到Cars和TestResults之间的JOIN组合,但问题是这返回了CAR ID为NULL缺少测试,因为与Car表没有匹配。

2 个答案:

答案 0 :(得分:4)

您可以从汽车交叉连接到安全测试 - 然后将其连接到TestResults - 交叉连接确保您获得10 x 20矩阵,左连接为您提供结果

使用ISNULL或COALESCE用零替换结果集中的空值

e.g。

SELECT car.name, testresults.testname, isnull(testresults.result, 0) FROM 
cars CROSS JOIN
safetytests
LEFT JOIN testresults on safetytests.testid = testresults.testid AND car.id = testresults.carid

Here's the cross join working

像某人建议的那样,是一个非常好的网站

http://sqlfiddle.com/#!3/2bc73/2

答案 1 :(得分:3)

如果我正确理解了这个问题,你想要一个像这样的10x20结果集:

CarID   |   Test1   |   Test2   |   ....    |   Test20
-------------------------------------------------------
1       |   NULL    |   Fail    |   ....    |   true
2       |   2       |   Pass    |   ....    |   false

为此,我将利用SQL-Server 2008的PIVOT函数。

WITH Results AS
(   SELECT  cars.CarID,
            TestName,
            TestValue
    FROM    Cars
            CROSS JOIN SafetyTests s
            LEFT JOIN TestResults res
                ON res.CarID = Cars.CarID
                AND res.TestID = s.TestID
) 
SELECT  *
FROM    Results
        PIVOT
        (   MAX(TestValue)
            FOR TestName IN ([TesT1], [Test2], [Test3], [Test4]) 
            -- LIST ALL 20 TEST NAMES HERE
        ) pvt

这样做的缺点是你必须明确列出要转动的所有测试名称,否则它们不会显示为列,但是可以动态地执行此操作。下面与上面的查询基本完全相同,但我已动态生成所有列名列表并将其插入查询中。

DECLARE @SQL NVARCHAR(MAX) = ''

SELECT  @SQL = @SQL + ',' + QUOTENAME(TestName)
FROM    SafetyTests

SET @SQL = 'WITH Results AS
            (   SELECT  Cars.CarID,
                        TestName,
                        TestValue
                FROM    Cars
                        CROSS JOIN SafetyTests s
                        LEFT JOIN TestResults res
                            ON res.CarID = Cars.CarID
                            AND res.TestID = s.TestID
            ) 
            SELECT  *
            FROM    Results
                    PIVOT
                    (   MAX(TestValue)
                        FOR TestName IN (' + STUFF(@SQL, 1, 1, '') + ')
                    ) pvt'

EXECUTE SP_EXECUTESQL @SQL

SQL Fiddle