尝试根据列的值获取不同的数据集

Trying to get a different data set depending on value of a column

我正在尝试构建一个需要条件的 SQL 语句,但我不确定如何去做。我查看了 If Else 语句,但它不能 return 超过 1 个值。

这是我能解释的最好的方式:

需要:

PT.Due_Date,
PO.Initials,
PO.[Order No],
PO.[Ordered by],
PO.Supplier,
PT.QTY,
PT.[Description]

这是令人困惑的地方(无论如何对我来说):

IF (PO.Part Delivered == 0)
{
    /* All the required information has been retrieved. */
}
ELSE IF (PO.Part Delivered == 1)
{
    /* Get the following information from POD: */
    POD.[Delivery Date],
    POD.Delivered,
    POD.[Delivery Note],
    POD.[Received_By ]
}

关于表之间的关系

[PO].[Order No] = [PT].[Order No]
[PT].[ID] = [POD].[Purchase_Table_ID]

POD和PO之间没有关系。

作为前言:不要将 SQL 的 SELECT 查询视为可以在 row-by-row 基础上执行逻辑分支的地方,而是将其视为仅使用 set-operations 和连接来描述查询的地方。

(在 row-by-row 的基础上在关系数据库中做 任何事情 无论如何都是 invariably terrible)。


那么你要做的是:

  1. 获取 PO 行中的 所有 PO.[Part Delivered] = 0,我们称它们为 undeliveredPOs.
  2. 分别获取PO行中PO.[Part Delivered] = 1行的所有行,以及POD行中的INNER JOIN行,以及我们称这些为 deliveredPOsWithPODs
    • 由于您没有向我们展示您的 CREATE TABLE 陈述,我假设您的 POPOD table 有 1:0-1(one-to-zero-or-one) 关系使得:
      • PO.[Order No]PO 的单数(非复合)PRIMARY KEY.
      • POD.[Order No]POD 的单数(非复合)PRIMARY KEY 并且 FOREIGN KEYPO.
      • 此设计(您使用 FOREIGN KEY 列作为“子”table 中的 PRIMARY KEY)是一个基本的 SQL 数据库 设计模式 从一开始就防止异常(即重复)“子”数据发生。
  3. 然后将 undeliveredPOsdeliveredPOsWithPODs 行连接(或更确切地说:UNION)在一起。
    • 由于 deliveredPOsWithPODs 的列多于 undeliveredPOs,您需要将 NULL 值添加到 make-up 差异中。

如果您不熟悉:在 SQL 中,set-operators(UNIONINTERSECTEXCEPT)“垂直”连接数据行,而 JOIN 运算符“水平”连接数据列(如果有帮助的话)。

像这样,使用命名的 CTE:

WITH posWithPts AS (
    
    SELECT
        -- PO:
        PO.Initials,
        PO.[Order No],
        PO.[Ordered by],
        PO.Supplier,
        -- PT:
        PT.Due_Date,
        PT.QTY,
        PT.[Description]
    FROM
        PO
        INNER JOIN PT ON PO.[SomeKey] = PT.[SomeKey]
),
undeliveredPOs AS (
    SELECT
        *
    FROM
        posWithPts
    WHERE
        [Part Delivered] = 0
),
deliveredPOsWithPODs AS (
    SELECT
        p.*,
        POD.[Delivery Date],
        POD.Delivered,
        POD.[Delivery Note],
        POD.[Received_By]
    FROM
        posWithPts AS p
        INNER JOIN POD ON p.[Order No] = POD.[Order No]
    WHERE
        p.[Part Delivered] = 1
)
SELECT
    u.*,
    -- Add NULLs for columns absent in undeliveredPOs:
    NULL AS [Delivery Date],
    NULL AS Delivered,
    NULL AS [Delivery Note],
    NULL AS [Received_By]
FROM
    undeliveredPOs AS u
UNION ALL /* Using `UNION ALL` instead of `UNION` for performance */
SELECT
    d.*
FROM
    deliveredPOsWithPODs AS d

我上面的 SQL 使用 * 来避免重复列名和提高可读性,但如果这变成 production-query 那么你应该总是明确列出所有列名(是的,这很乏味)并使用 WITH SCHEMABINDING 来防止数据库 table 设计更改破坏您的查询 and/or 导致不正确的结果 - 这在使用 UNION 和不匹配的列时最重要。


如果 query-maintenance 不是问题,那么可以内联最后 2 个 CTE(undeliveredPOsdeliveredPOsWithPODs)(但因为 posWithPts(源 view/CTE/derived-table/query) 被多次引用,它将保留为命名 CTE),因此上面的查询可以简化为这个

WITH posWithPts AS (
    
    SELECT
        -- PO:
        PO.Initials,
        PO.[Order No],
        PO.[Ordered by],
        PO.Supplier,
        -- PT:
        PT.Due_Date,
        PT.QTY,
        PT.[Description]
    FROM
        PO
        INNER JOIN PT ON PO.[SomeKey] = PT.[SomeKey]
),
SELECT
    n.*,
    -- Add NULLs for columns absent in undeliveredPOs:
    NULL AS [Delivery Date],
    NULL AS Delivered,
    NULL AS [Delivery Note],
    NULL AS [Received_By]
FROM
    posWithPts AS n
WHERE
    n.[Part Delivered] = 0
UNION ALL
SELECT
    d.*,
    POD.[Delivery Date],
    POD.Delivered,
    POD.[Delivery Note],
    POD.[Received_By]
FROM
    posWithPts AS d
    INNER JOIN POD ON p.[Order No] = POD.[Order No]
WHERE
    d.[Part Delivered] = 1