1

私の組織では、クライアントは一度に複数のプログラムに登録できます。クライアントが一意の行として登録されているすべてのプログラムのリストと、そのプログラムに登録された日付を含むテーブルがあります。

外部結合を使用すると、テーブル (クライアントが完了したテストのテーブルなど) から任意のクライアント名と日付を取得し、その特定の日付にクライアントが参加していたすべてのプログラムを返すことができます。クライアントがその日に複数のプログラムに参加していた場合、その日に参加していた各プログラムのテーブルのデータが複製されます。

私が抱えている問題は、その日に複数のプログラムに参加していたとしても、クライアントと日付ごとに「プライマリ プログラム」として 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

より大きな問題は、私の実際のテーブルには、可能なプログラムが 4 つだけではなく、CASE WHEN select ステートメントと 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()null 以外の最初の値を返します。

設計上、s は引き続き必要ですが、たとえば代わり​​に非常に短いエイリアスを使用すると、コード ノイズJOINが大幅に少なくなり、読みやすくなります。PP1PreviousPrograms1

于 2013-08-07T22:49:16.603 に答える
2

すべてのプログラムの種類とその優先度を含むブリッジ テーブルを使用して、結合を簡素化できます (私の SQL サーバーの構文は少しさびています)。

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

このテーブルにはすべてのプログラム タイプが保持され、プログラムの優先度は質問で指定した優先度を反映します。

ケースの一部については、関連するレコードの数によって異なります。私が通常行うトリックの 1 つは次のとおりです (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 番号またはその他の患者 ID を含める必要があると思います。それを出力として両方のサブ選択に追加し、結合を変更するだけです。

于 2013-08-07T23:22:55.820 に答える