使用临时表的动态数据透视表

Dynamic Pivot using Temp tables

我有三个查询,它们都构建了三个独立的 table。程序、DRG 和诊断。我不知道患者可能有多少手术、工作 DRG 或诊断。然而,我只需要某些程序代码、DRG 和诊断代码。我在临时 table 中对每个条目进行了排名。我们现在只使用一个 table,因为其他两个的答案是一样的。让我们只处理诊断 Table.

在诊断中 Table 我有一个排名字段,它只会在 Dx_Rank 字段中输入 Dx_1、Dx_2、Dx_3所以我可以把它们拉出来,但我不知道会有多少Dx。我需要这些都在一条线上。所以输出会是这样的。

Patient_Account, Visit_Key, Dx_1, Dx_1_描述, Dx_2, Dx_2_描述, Dx_3, Dx_3_描述

来自 table 看起来像这样

> Patient_Account, Visit_Key, Dx_Code, Dx_Priority, Dx_Rank, Dx_Desctiption  
> 123456789, #PAS203234, J20, 3, Dx_1, Left Hip is Broken 
> 123456789, #PAS203234, A32, 6, Dx_2, Left Knee is broken 
> 123456789, #PAS203234, R4786, 8, Dx_3, Left Ankle is broken 
> 987654321, #PAS435678, DF346, 2, Dx_1, Right Arm is broken  
> 987654321, #PAS435678, DT342.12, 4, Dx_2, Right Wrist is broken

这里我有两个患者,输出应该是这样的。

Patient_Account, Visit_Key, Dx_1, Dx_1_Description, Dx_2, Dx_2_Description, Dx_3, Dx_3_Description

123456789, #PAS203234, J20, Left Hip is broken, A32, Left Knee is broken, R4786, Left Ankle is broken
987654321, #PAS435678, DF346, Right Arm is broken, DT342.12, Right Wrist is broken, , ,

所以我想我会做一个 PIVOT。我设置了一切,但它没有给我我想要的。我在右栏中得到 Dx_1,但是当有 Dx_2 时,它在右栏中,但它在单独的一行中。我希望这一切都在一行中,因为最终输出将进入 Excel 并且我想让它动态化所以如果我得到的最大诊断是 Dx_3 它会停止并且没有 7 个额外的空白列.如果我将其硬编码为每个都有 10 个,然后当我最终得到一个有 11 个的患者时会发生什么,而我错过了一个,因为我有 10 个诊断的限制。

这是我目前的脚本。

IF OBJECT_ID('tempdb..#PROCEDURES_CPT') IS NOT NULL 
    DROP TABLE #PROCEDURES_CPT
IF OBJECT_ID('tempdb..#PROCEDURES_CPT_PIVOT') IS NOT NULL 
DROP TABLE #PROCEDURE_CPT_PIVOT

IF OBJECT_ID('tempdb..#PROCEDURES_DRG') IS NOT NULL 
DROP TABLE #PROCEDURES_DRG
IF OBJECT_ID('tempdb..#PROCEDURES_DRG_PIVOT') IS NOT NULL 
DROP TABLE #PROCEDURES_DRG_PIVOT

IF OBJECT_ID('tempdb..#PROCEDURES_DX') IS NOT NULL 
DROP TABLE #PROCEDURES_DX
IF OBJECT_ID('tempdb..#PROCEDURES_DX_PIVOT') IS NOT NULL 
DROP TABLE #PROCEDURES_DX_PIVOT

IF OBJECT_ID('tempdb..#PATIENTS') IS NOT NULL 
DROP TABLE #PATIENTS
IF OBJECT_ID('tempdb..#PATIENTS_FINAL') IS NOT NULL 
DROP TABLE #PATIENTS_FINAL

/** DEFINE TABLE AND PARAMETERS  **/

/** CREATE THE TABLE FOR THE PATIENTS  **/
CREATE TABLE #PATIENTS
(
        Patient_Account VARCHAR(20) NOT NULL,
        Visit_Key       VARCHAR(20) NOT NULL
)

/**  DECLARE THE VARIABLES FOR THE TEMP PIVOT TABLES THAT ARE NEEDED FOR THE THREE SECTIONS  **/
DECLARE @SQLPIVOT_CPT AS NVARCHAR(MAX)
DECLARE @SQLPIVOT_DRG AS NVARCHAR(MAX)
DECLARE @SQLPIVOT_DX AS NVARCHAR(MAX)

/**  DECLARE THE VARIABLE NAME THAT WILL HOLD THE COLUMN NAMES LIKE CPT_1, CPT2, CPT_3, etc OR DRG_1, DRG_2,etc, OR DX_1, DX_2, DX_3, DX_4, etc  **/
DECLARE @PivotColumns_CPT AS NVARCHAR(MAX)
DECLARE @PivotColumns_DRG AS NVARCHAR(MAX)
DECLARE @PivotColumns_DX AS NVARCHAR(MAX)


/** CPT CODES  **/

SELECT      pv.pt_id,
            pv.vst_key,
            pv.proc_eff_full_date,
            pv.prio_cd,
            'CPT_' + CONVERT(VARCHAR(10), ROW_NUMBER() OVER(PARTITION BY pv.pt_id ORDER BY pv.prio_cd)) AS 'CPT_Rank',
            pv.proc_cd,
            pv.alt_clasf_desc,
            hrp.Procedure_Type,
            UPPER(pv.resp_pract_rpt_name) AS 'Surgeon'
INTO        #PROCEDURES_CPT
FROM        smsdss.proc_v AS pv INNER JOIN
            dbo.BETHESDA_HIGH_RISK_PROCEDURES AS hrp ON pv.proc_cd = hrp.Clinical_Code AND hrp.Report_Type = 'CPT'
WHERE       pv.proc_eff_full_date BETWEEN '10/01/2020' AND '09/30/2021'

/** DRG CODES  **/

SELECT      vv.pt_id,
            vv.vst_key,
            vv.end_full_date,
            vv.drg_no,
            UPPER(drg.DRGDesc) AS 'DRG_Description',
            hrp.Procedure_Type,
            UPPER(vv.adm_pract_rpt_name) AS 'Admitting Physician'
INTO        #PROCEDURES_DRG
FROM        smsdss.vst_v AS vv INNER JOIN
            dbo.BETHESDA_HIGH_RISK_PROCEDURES AS hrp ON vv.drg_no = hrp.Clinical_Code AND hrp.Report_Type = 'DRG' LEFT OUTER JOIN
            smsdss.DRGMstr AS drg ON vv.drg_no = drg.DRGNo  AND drg.DRGVers = 'MS-V38'
WHERE       vv.end_full_date BETWEEN '10/01/2020' AND '09/30/2021'

/** DX CODES  **/

SELECT      dx.pt_id,
            dx.vst_key,
            dx.dx_eff_full_date,
            dx.dx_type_desc,
            'Dx_' + CONVERT(VARCHAR(10), ROW_NUMBER() OVER(PARTITION BY dx.pt_id, dx.dx_type_desc ORDER BY dx.prio_cd)) AS 'Dx_Rank',
            dx.prio_cd,
            dx.dx_cd,
            dx.clasf_desc,
            hrp.Procedure_Type 
INTO        #PROCEDURES_DX
FROM        smsdss.dx_grp_v AS dx INNER JOIN
            dbo.BETHESDA_HIGH_RISK_PROCEDURES AS hrp ON dx.dx_cd = hrp.Clinical_Code AND hrp.Report_Type = 'ICD-10'
WHERE       dx.dx_eff_full_date BETWEEN '10/01/2020' AND '09/30/2021'
GROUP BY    dx.pt_id,
            dx.vst_key,
            dx.dx_eff_full_date,
            dx.dx_type_desc,
            dx.prio_cd,
            dx.dx_cd,
            dx.clasf_desc,
            hrp.Procedure_Type 
ORDER BY    dx.pt_id,
            dx.dx_type_desc,
            dx.prio_cd
 
 /** PIVOT DX  **/
 SELECT     @PivotColumns_DX = COALESCE(@PivotColumns_DX +  ', ' , '') + Dx_Rank
 FROM       #PROCEDURES_DX 
 GROUP BY   Dx_Rank
 ORDER BY   Dx_Rank

SELECT @PivotColumns_DX

 SET @SQLPIVOT_DX = N'SELECT    pt_id, vst_key, ' + @PivotColumns_DX + ' 
                      INTO      #PROCEDURES_DX_PIVOT
                      FROM      #PROCEDURES_DX
                            PIVOT (MAX(dx_cd)
                                FOR Dx_Rank IN (' + @PivotColumns_DX + ')) AS tdx'
SELECT @SQLPIVOT_DX

EXEC sp_executesql @SQLPIVOT_DX

SELECT * FROM #PROCEDURES_DX_PIVOT

直到我在最后一行添加尝试 select 来自 Temp Table #PROCEDURES_DX_PIVOT 以查看我有什么之前,我没有收到任何错误。当我 运行 对象无效的代码时出现错误。就像代码没有被执行一样。我创建了一个简单的测试来查看它是否是一个权限,即使我没有收到错误并且我能够创建一些东西。然后我从那个 SELECT @SQLPIVOT_DX 中获取输出,那时我能够看到我的结果以及我如何看到患者是否有 Dx_1 和 Dx_2 它们在两条不同的线上这不是我想要的。我正在尝试将所有内容放在一条线上。

如有任何帮助,我们将不胜感激。如果我的方法不对请告诉我。

美好的一天,很遗憾,您没有提供所有相关实体来重现您的场景并执行您的完整查询,但最后一个主要问题似乎很清楚

I am not getting any errors until I add in the last line a try a select from the Temp Table #PROCEDURES_DX_PIVOT to see what I have. I get an error when I run the code that the Object is not Valid. It is like the code is not being executed.

问题是您在 sp_executesql 的执行范围内创建了临时 table,但您尝试在此范围之外使用它,但它不存在

这是同一问题的简单完整演示

准备

USE tempdb
GO
DROP TABLE IF EXISTS T
GO 
create table T(id int)
GO

工作示例,我们在其中创建临时 table 在我们调用它的相同范围内

DROP TABLE IF EXISTS #PROCEDURES_DX_PIVOT
GO 
SELECT * INTO #PROCEDURES_DX_PIVOT FROM T
GO
SELECT * FROM #PROCEDURES_DX_PIVOT
GO -- OK, since the temp table was created in the same scope as we call it

错误!无效的对象名称'#PROCEDURES_DX_PIVOT'

DROP TABLE IF EXISTS #PROCEDURES_DX_PIVOT
GO 
DECLARE @SQLPIVOT_DX NVARCHAR(MAX)
SET @SQLPIVOT_DX = N'SELECT * INTO #PROCEDURES_DX_PIVOT FROM T'
SELECT @SQLPIVOT_DX
EXEC sp_executesql @SQLPIVOT_DX

SELECT * FROM #PROCEDURES_DX_PIVOT
GO -- Invalid object name '#PROCEDURES_DX_PIVOT'

可选的解决方案是从动态查询内部调用 table

DROP TABLE IF EXISTS #PROCEDURES_DX_PIVOT
GO 
DECLARE @SQLPIVOT_DX NVARCHAR(MAX)
SET @SQLPIVOT_DX = N'SELECT * INTO #PROCEDURES_DX_PIVOT FROM T; SELECT * FROM #PROCEDURES_DX_PIVOT;'
SELECT @SQLPIVOT_DX
EXEC sp_executesql @SQLPIVOT_DX
GO -- Invalid object name '#PROCEDURES_DX_PIVOT'

返回您的代码

这与您的代码中的问题相同:

SET @SQLPIVOT_DX = N'SELECT    pt_id, vst_key, ' + @PivotColumns_DX + ' 
                      INTO      #PROCEDURES_DX_PIVOT
                      FROM      #PROCEDURES_DX
                            PIVOT (MAX(dx_cd)
                                FOR Dx_Rank IN (' + @PivotColumns_DX + ')) AS tdx'
SELECT @SQLPIVOT_DX

EXEC sp_executesql @SQLPIVOT_DX -- here you use select into to create the temp table

SELECT * FROM #PROCEDURES_DX_PIVOT --  and here you use it outside of the scope which it was created

要在同一行中获取 Patient_Account 的每个 Dx_Code,您需要 GROUP BY Patient_Account,并使用一些聚合函数(如 MIN() 或 MAX())来得到 Dx_Code 和 Dx_Desctiption.

进行动态查询的另一种方法:

SELECT STRING_AGG(query_piece, '')

FROM (

(SELECT 'SELECT Patient_Account,' AS query_piece)

UNION ALL

(SELECT 
    CONCAT('MIN(CASE WHEN Dx_Rank = ''', Dx_Rank, ''' THEN Dx_Code END) AS ', Dx_Rank, ', ', 
    'MIN(CASE WHEN Dx_Rank = ''', Dx_Rank, ''' THEN Dx_Desctiption END) AS ', Dx_Rank, '_Description, ')
FROM Diagnosis
GROUP BY Dx_Rank)

UNION ALL

(SELECT 'Visit_Key 
    FROM Diagnosis
    GROUP BY Patient_Account, Visit_Key;')) AS dynamic_query;

动态查询的结果是您必须执行以获得所需结果的查询:

SELECT Patient_Account,MIN(CASE WHEN Dx_Rank = 'Dx_1' THEN Dx_Code END) AS Dx_1, MIN(CASE WHEN Dx_Rank = 'Dx_1' THEN Dx_Desctiptio END) AS Dx_1_Description, MIN(CASE WHEN Dx_Rank = 'Dx_2' THEN Dx_Code END) AS Dx_2, MIN(CASE WHEN Dx_Rank = 'Dx_2' THEN Dx_Desctiptio END) AS Dx_2_Description, MIN(CASE WHEN Dx_Rank = 'Dx_3' THEN Dx_Code END) AS Dx_3, MIN(CASE WHEN Dx_Rank = 'Dx_3' THEN Dx_Desctiptio END) AS Dx_3_Description, Visit_Key 
    FROM Diagnosis
    GROUP BY Patient_Account, Visit_Key;

输出问题中的示例数据:

Patient_Account Dx_1 Dx_1_Description Dx_2 Dx_2_Description Dx_3 Dx_3_Description Visit_Key
123456789 J20 Left Hip is Broken A32 Left Knee is broken R4786 Left Ankle is broken #PAS203234
987654321 DF346 Right Arm is broken DT342.12 Right Wrist is broken #PAS435678

Sql-Server 2016中的动态查询(无string_agg()函数):

SELECT STUFF((  
        SELECT ' ' + query_piece  
        FROM ((SELECT 'SELECT Patient_Account,' AS query_piece)

        UNION ALL

        (SELECT TOP 100
            CONCAT('MIN(CASE WHEN Dx_Rank = ''', Dx_Rank, ''' THEN Dx_Code END) AS ', Dx_Rank, ', ', 
            'MIN(CASE WHEN Dx_Rank = ''', Dx_Rank, ''' THEN Dx_Desctiption END) AS ', Dx_Rank, '_Description, ')
        FROM Diagnosis
        GROUP BY Dx_Rank
        ORDER BY Dx_Rank ASC)

        UNION ALL

        (SELECT 'Visit_Key FROM Diagnosis GROUP BY Patient_Account, Visit_Key')) as x
        FOR XML PATH('')
     ), 1, 1, '');