1

在我的组织中,客户可以一次注册多个项目。我有一个表格,其中列出了客户作为唯一行注册的所有计划以及他们注册该计划的日期。

使用外部联接,我可以从表中获取任何客户名称和日期(比如客户已完成的测试表),并让它返回客户在该特定日期参与的所有程序。如果客户在该日期参加了多个项目,它会为他们在该日期参加的每个项目复制该表中的数据。

我遇到的问题是,我希望它只为每个客户和日期返回一个程序作为他们的“主要程序”,即使他们在那个日期在多个程序中也是如此。我已经创建了一个层次结构,应该选择哪个程序作为他们的主要程序并返回。

例如:

1.)住院病人

2.)门诊临床

3.)门诊职业

4.)门诊娱乐

因此,如果客户在该日期同时注册了门诊临床、门诊职业、门诊娱乐,它只会返回“门诊临床”作为程序。

我这样做的想法是多次加入到以前的程序的表中,如下所示:

FROM dbo.TestTable as TestTable

LEFT OUTER JOIN dbo.PreviousPrograms as PreviousPrograms1
ON TestTable.date = PreviousPrograms1.date AND PreviousPrograms1.type = 'Inpatient'

LEFT OUTER JOIN dbo.PreviousPrograms as PreviousPrograms2
ON TestTable.date = PreviousPrograms2.date AND PreviousPrograms2.type = 'Outpatient Clinical'

LEFT OUTER JOIN dbo.PreviousPrograms as PreviousPrograms3
ON TestTable.date = PreviousPrograms3.date AND PreviousPrograms3.type = 'Outpatient Vocational'

LEFT OUTER JOIN dbo.PreviousPrograms as PreviousPrograms4
ON TestTable.date = PreviousPrograms4.date AND PreviousPrograms4.type = 'Outpatient Recreational'

然后在 SELECT 语句中执行条件 CASE WHEN,如下所示:

SELECT

CASE 
        WHEN PreviousPrograms1.name IS NOT NULL
            THEN PreviousPrograms1.name
        WHEN PreviousPrograms1.name IS NULL AND PreviousPrograms2.name IS NOT NULL
            THEN PreviousPrograms2.name
        WHEN PreviousPrograms1.name IS NULL AND PreviousPrograms2.name IS NULL AND PreviousPrograms3.name IS NOT NULL
            THEN PreviousPrograms3.name
        WHEN PreviousPrograms1.name IS NULL AND PreviousPrograms2.name IS NULL AND PreviousPrograms3.name IS NOT NULL AND PreviousPrograms4.name IS NOT NULL
            THEN PreviousPrograms4.name
        ELSE NULL
        END as PrimaryProgram

更大的问题是,在我的实际表中,它可能不仅仅是四个可能的程序,而且 CASE WHEN 选择语句和 JOIN 已经够麻烦了。

是否有更有效的方法来执行 SELECTs 部分或 JOIN 部分?或者可能是一个更好的方法来一起完成这一切?

我正在使用 SQL Server 2008。

4

3 回答 3

3

您可以CASE通过使用来简化(替换)您的COALESCE()

SELECT
  COALESCE(PreviousPrograms1.name, PreviousPrograms2.name,
    PreviousPrograms3.name, PreviousPrograms4.name) AS PreviousProgram

COALESCE()返回第一个非空值。

由于您的设计,您仍然需要JOINs,但如果您使用非常短的别名,例如PP1而不是PreviousPrograms1- 它会更容易阅读代码噪音。

于 2013-08-07T22:49:16.603 回答
2

您可以通过使用包含所有程序类型及其优先级的桥接表来简化联接(我的 sql 服务器语法有点生疏):

create table BridgeTable (
    programType varchar(30),
    programPriority smallint
);

此表将包含所有程序类型,程序优先级将反映您在问题中指定的优先级。

至于案件的部分,这将取决于所涉及的记录数量。我通常做的一个技巧是这个(假设 programPriority 是一个介于 10 和 99 之间的数字,并且没有类型可以有超过 30 个字节,因为我很懒惰):

Select patient, date, 
                substr( min(cast(BridgeTable.programPriority as varchar) || PreviousPrograms.type), 3, 30) 
From dbo.TestTable as TestTable
Inner Join dbo.BridgeTable as BridgeTable
Left Outer Join dbo.PreviousPrograms as PreviousPrograms 
                on PreviousPrograms.type = BridgeTable.programType 
                   and TestTable.date = PreviousPrograms.date
Group by patient, date
于 2013-08-07T21:17:01.340 回答
2

您可以使用子查询来实现这一点,或者您可以重构它以使用 CTE,看看下面的内容,看看它是否有意义:

DECLARE @testTable TABLE
(
    [id] INT IDENTITY(1, 1),
    [date] datetime
)
DECLARE @previousPrograms TABLE
(
    [id] INT IDENTITY(1,1),
    [date] datetime,
    [type] varchar(50)
)

INSERT INTO @testTable ([date])
SELECT '2013-08-08'
UNION ALL SELECT '2013-08-07'
UNION ALL SELECT '2013-08-06'

INSERT INTO @previousPrograms ([date], [type])
-- a sample user as an inpatient
SELECT '2013-08-08', 'Inpatient'
-- your use case of someone being enrolled in all 3 outpation programs
UNION ALL SELECT '2013-08-07', 'Outpatient Recreational'
UNION ALL SELECT '2013-08-07', 'Outpatient Clinical'
UNION ALL SELECT '2013-08-07', 'Outpatient Vocational'

-- showing our workings, this is what we'll join to
SELECT 
    PPP.[date],
    PPP.[type],
    ROW_NUMBER() OVER (PARTITION BY PPP.[date] ORDER BY PPP.[Priority]) AS [RowNumber]
FROM (
    SELECT
        [type],
        [date],
        CASE 
            WHEN [type] = 'Inpatient' THEN 1
            WHEN [type] = 'Outpatient Clinical' THEN 2
            WHEN [type] = 'Outpatient Vocational' THEN 3
            WHEN [type] = 'Outpatient Recreational' THEN 4
            ELSE 999
        END AS [Priority]
    FROM @previousPrograms
) PPP -- Previous Programs w/ Priority

SELECT
    T.[date],
    PPPO.[type]
FROM @testTable T
LEFT JOIN (
    SELECT 
        PPP.[date],
        PPP.[type],
        ROW_NUMBER() OVER (PARTITION BY PPP.[date] ORDER BY PPP.[Priority]) AS [RowNumber]
    FROM (
        SELECT
            [type],
            [date],
            CASE 
                WHEN [type] = 'Inpatient' THEN 1
                WHEN [type] = 'Outpatient Clinical' THEN 2
                WHEN [type] = 'Outpatient Vocational' THEN 3
                WHEN [type] = 'Outpatient Recreational' THEN 4
                ELSE 999
            END AS [Priority]
        FROM @previousPrograms
    ) PPP -- Previous Programs w/ Priority
) PPPO -- Previous Programs w/ Priority + Order
    ON T.[date] = PPPO.[date] AND PPPO.[RowNumber] = 1

基本上,我们有最深的子选择,根据类型为所有 PreviousPrograms 赋予优先级,然后我们的包装子选择为它们提供每个日期的行号,因此我们只能选择行号为 1 的那些。

我猜您需要包含 UR 编号或其他一些患者标识符,只需将其作为输出添加到两个子选择并更改连接即可。

于 2013-08-07T23:22:55.820 回答