【问题标题】:What kind of JOIN do I need to perform this type of query?我需要什么样的 JOIN 来执行这种类型的查询?
【发布时间】:2012-05-18 08:58:05
【问题描述】:

假设我有三个表:

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

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

我想生成一个 10 x 20 矩阵,它将显示每辆车 20 个结果(即使他们没有 20 个安全测试结果)。如果尚未在汽车上执行测试,它只会显示测试名称,但值为零(或 null)。

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

【问题讨论】:

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


    【解决方案1】:

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

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

    例如

    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
    

    按照某人的建议做了一个 sqlfiddle :) 那个网站真的很好

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

    【讨论】:

    • 粉碎,非常感谢!我没有意识到有交叉连接这样的事情。当我上周查看 joins 时,我从未在 W3Schools 页面上看到过这个!它是否还有其他名称,例如 Full Join 等??
    • 没有完全连接是不同的连接 - 交叉连接只是行的笛卡尔积,不需要任何连接条件(我已经更新了左连接,因为它缺少汽车连接试验结果)。 CROSS JOIN 总是给出(左行数)*(右行数)
    • 在我的数据库上模拟表并添加图像以证明它有效:)
    • 昨天在弄乱了jsfiddle之后看到了sqlfiddle,看起来不错,会尝试一下
    【解决方案2】:

    如果我正确理解了这个问题,您想要一个 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

    【讨论】:

    • 不管它是否是正确的答案,但它是使用 STUFF 进行 PIVOT 和动态 PIVOT 的一个很好的例子,所以 +1
    猜你喜欢
    • 2018-01-28
    • 1970-01-01
    • 2011-07-13
    • 2012-08-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多