对非数值数据使用 Pivot
Using Pivot with non Numerical Data
这是我第一次尝试使用 PIVOT。
我正在使用 Microsoft SQL 服务器。
所以这是我的问题,我一直在阅读 Pivot 并决定它对于将患者数据导出到格式化文件(即报告)的项目非常有用,可以打印出来等等。 .
VPatientPlusAllergyData 是一个视图,将此显示为样本结果,为了便于阅读,一些数据被删减
strPatientFullName strAllergy strAllergyMedication
------------------------------------------------------------
Smith, John Henry Dogs Pounces
Smith, John Henry Dogs Orange Juice
Smith, John Henry Mustard Ketchup
Smith, John Henry Mustard Sugar
这就是我想要的结果
strPatientFullName strAllergy1 strAllergy1Medications strAllergy2 strAllergy2Medications
------------------------------------------------------------------------------------------------------
Smith, John Henry Dogs Pounces, OrangeJuice Mustard Ketchup, Sugar
在 W3School 上阅读、观看 Youtube 视频甚至阅读本网站上的一些文章后,我想知道我正在尝试做的事情是否可行
下面是一个代码片段,但我卡在了我应该在 IN 语句中放入什么,当我开始质疑 PIVOT 是否能解决我的特定问题时。
GO
SELECT
strPatientFullName
,strStreetAddress
,strCity
,strState
,strZipcode
,strPrimaryPhoneNumber
,strSecondaryPhoneNumber
,blnSmoker
,decPackYears
,blnHeadOfHousehold
,dtmDateOfBirth
,strSex
,strAllergy
,strAllergyMedication
,strEmailAddress
,strRecordCreator
FROM ( SELECT * FROM VPatientPlusAllergyData ) PatientAllergyData
PIVOT
(
MAX(strAllergyMedication)
FOR strAllergy
IN ()
)
GO
希望更熟悉 Pivot 的人能告诉我我遗漏了什么,或者启发我找到更有效的解决方案。
感谢您的帮助
****** 编辑:我已经决定虽然我很乐意将这种操作放在服务器端,但对于我的特定应用程序,创建大量视图然后执行 SELECT 在客户端查询并以这种方式连接它们,然后实现 "EXPORT PROCESSING" 屏幕。
我感谢所有的帮助,也许有一天我会写一个脚本并让它在服务器端执行,但目前这项工作已经足够好了 ******
这是一个示例,说明如何使用 STUFF 语句、条件聚合和动态 SQL。
DECLARE @SQL NVARCHAR(MAX) = '';
SELECT @SQL += '
, MAX(CASE WHEN RN = ' + RN + ' THEN strAllergy END) strAllergy' + RN + '
, MAX(CASE WHEN RN = ' + RN + ' THEN strAllergyMedications END) strAllergyMedications' + RN
FROM (
SELECT CAST(ROW_NUMBER() OVER (PARTITION BY strPatientFullName, strAllergy ORDER BY (SELECT NULL)) AS VARCHAR(5)) RN
FROM VPatientPlusAllergyData) T
GROUP BY RN;
SELECT @SQL = 'SELECT strPatientFullName' + @SQL + '
FROM (
SELECT strPatientFullname
, strAllergy
, STUFF((SELECT '', '' + strAllergyMedication FROM VPatientPlusAllergyData WHERE strPatientFullName = T.strPatientFullName AND strAllergy = T.strAllergy FOR XML PATH ('''')), 1, 2, '''') strAllergyMedications
, ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) RN
FROM VPatientPlusAllergyData T
GROUP BY strPatientFullname, strAllergy) T
GROUP BY strPatientFullname;';
PRINT @SQL;
EXEC(@SQL);
正如 scsimon 在评论中提到的,如果可能存在任意数量的过敏,则可能需要动态 SQL。 stuff 语句是将逗号分隔值放入单个列中的一种方法。条件聚合的工作方式与 PIVOT 通常工作的方式相同,但比普通 PIVOT 语句更容易 (IMO) 编写和理解。
因此,要获得您想要的结果,您实际上需要以下技术:
- 对于 strAllergyMedications,您需要 将行连接到定界字符串
- 然后要将您的行变成列,您需要
PIVOT
,但是因为您要旋转 2 列,所以您必须 PIVOT
两次或使用 条件聚合
实现它的主要技巧是通过进行串联并提出过敏的行号来准备您的 table。下面是一个使用 Common Table Expression [CTE] 和 STUFF() 以及子 select XML 来创建分隔字符串并创建行号的示例。
DECLARE @VPatientPlusAllergyData AS TABLE (strPatientFullName VARCHAR(100), strAllergy VARCHAR(50), strAllergyMedication VARCHAR(100))
INSERT INTO @VPatientPlusAllergyData VALUES
('Smith, John Henry','Dogs','Pounces')
,('Smith, John Henry','Dogs','Orange Juice')
,('Smith, John Henry','Mustard','Ketchup')
,('Smith, John Henry','Mustard','Sugar')
;WITH cte AS (
SELECT DISTINCT
v1.strPatientFullName
,v1.strAllergy
,strAllergyMedications = STUFF(
(SELECT ', ' + v2.strAllergyMedication
FROM
@VPatientPlusAllergyData v2
WHERE
v1.strPatientFullName = v2.strPatientFullName
AND v1.strAllergy = v2.strAllergy
FOR XML PATH(''))
,1,2,'')
,AllergyRowNum = DENSE_RANK() OVER (PARTITION BY v1.strPatientFullName ORDER BY v1.strAllergy)
FROM
@VPatientPlusAllergyData v1
)
SELECT
strPatientFullName
,strAllergy1 = MAX(CASE WHEN AllergyRowNum = 1 THEN strAllergy END)
,strAllergy1Medications = MAX(CASE WHEN AllergyRowNum = 1 THEN strAllergyMedications END)
,strAllergy2 = MAX(CASE WHEN AllergyRowNum = 2 THEN strAllergy END)
,strAllergy2Medications = MAX(CASE WHEN AllergyRowNum = 2 THEN strAllergyMedications END)
FROM
cte
GROUP BY
strPatientFullName
在我准备和发布这篇文章时,@ZLK 写了一个很好的动态方法。
这是我第一次尝试使用 PIVOT。 我正在使用 Microsoft SQL 服务器。
所以这是我的问题,我一直在阅读 Pivot 并决定它对于将患者数据导出到格式化文件(即报告)的项目非常有用,可以打印出来等等。 .
VPatientPlusAllergyData 是一个视图,将此显示为样本结果,为了便于阅读,一些数据被删减
strPatientFullName strAllergy strAllergyMedication
------------------------------------------------------------
Smith, John Henry Dogs Pounces
Smith, John Henry Dogs Orange Juice
Smith, John Henry Mustard Ketchup
Smith, John Henry Mustard Sugar
这就是我想要的结果
strPatientFullName strAllergy1 strAllergy1Medications strAllergy2 strAllergy2Medications
------------------------------------------------------------------------------------------------------
Smith, John Henry Dogs Pounces, OrangeJuice Mustard Ketchup, Sugar
在 W3School 上阅读、观看 Youtube 视频甚至阅读本网站上的一些文章后,我想知道我正在尝试做的事情是否可行
下面是一个代码片段,但我卡在了我应该在 IN 语句中放入什么,当我开始质疑 PIVOT 是否能解决我的特定问题时。
GO
SELECT
strPatientFullName
,strStreetAddress
,strCity
,strState
,strZipcode
,strPrimaryPhoneNumber
,strSecondaryPhoneNumber
,blnSmoker
,decPackYears
,blnHeadOfHousehold
,dtmDateOfBirth
,strSex
,strAllergy
,strAllergyMedication
,strEmailAddress
,strRecordCreator
FROM ( SELECT * FROM VPatientPlusAllergyData ) PatientAllergyData
PIVOT
(
MAX(strAllergyMedication)
FOR strAllergy
IN ()
)
GO
希望更熟悉 Pivot 的人能告诉我我遗漏了什么,或者启发我找到更有效的解决方案。
感谢您的帮助
****** 编辑:我已经决定虽然我很乐意将这种操作放在服务器端,但对于我的特定应用程序,创建大量视图然后执行 SELECT 在客户端查询并以这种方式连接它们,然后实现 "EXPORT PROCESSING" 屏幕。 我感谢所有的帮助,也许有一天我会写一个脚本并让它在服务器端执行,但目前这项工作已经足够好了 ******
这是一个示例,说明如何使用 STUFF 语句、条件聚合和动态 SQL。
DECLARE @SQL NVARCHAR(MAX) = '';
SELECT @SQL += '
, MAX(CASE WHEN RN = ' + RN + ' THEN strAllergy END) strAllergy' + RN + '
, MAX(CASE WHEN RN = ' + RN + ' THEN strAllergyMedications END) strAllergyMedications' + RN
FROM (
SELECT CAST(ROW_NUMBER() OVER (PARTITION BY strPatientFullName, strAllergy ORDER BY (SELECT NULL)) AS VARCHAR(5)) RN
FROM VPatientPlusAllergyData) T
GROUP BY RN;
SELECT @SQL = 'SELECT strPatientFullName' + @SQL + '
FROM (
SELECT strPatientFullname
, strAllergy
, STUFF((SELECT '', '' + strAllergyMedication FROM VPatientPlusAllergyData WHERE strPatientFullName = T.strPatientFullName AND strAllergy = T.strAllergy FOR XML PATH ('''')), 1, 2, '''') strAllergyMedications
, ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) RN
FROM VPatientPlusAllergyData T
GROUP BY strPatientFullname, strAllergy) T
GROUP BY strPatientFullname;';
PRINT @SQL;
EXEC(@SQL);
正如 scsimon 在评论中提到的,如果可能存在任意数量的过敏,则可能需要动态 SQL。 stuff 语句是将逗号分隔值放入单个列中的一种方法。条件聚合的工作方式与 PIVOT 通常工作的方式相同,但比普通 PIVOT 语句更容易 (IMO) 编写和理解。
因此,要获得您想要的结果,您实际上需要以下技术:
- 对于 strAllergyMedications,您需要 将行连接到定界字符串
- 然后要将您的行变成列,您需要
PIVOT
,但是因为您要旋转 2 列,所以您必须PIVOT
两次或使用 条件聚合
实现它的主要技巧是通过进行串联并提出过敏的行号来准备您的 table。下面是一个使用 Common Table Expression [CTE] 和 STUFF() 以及子 select XML 来创建分隔字符串并创建行号的示例。
DECLARE @VPatientPlusAllergyData AS TABLE (strPatientFullName VARCHAR(100), strAllergy VARCHAR(50), strAllergyMedication VARCHAR(100))
INSERT INTO @VPatientPlusAllergyData VALUES
('Smith, John Henry','Dogs','Pounces')
,('Smith, John Henry','Dogs','Orange Juice')
,('Smith, John Henry','Mustard','Ketchup')
,('Smith, John Henry','Mustard','Sugar')
;WITH cte AS (
SELECT DISTINCT
v1.strPatientFullName
,v1.strAllergy
,strAllergyMedications = STUFF(
(SELECT ', ' + v2.strAllergyMedication
FROM
@VPatientPlusAllergyData v2
WHERE
v1.strPatientFullName = v2.strPatientFullName
AND v1.strAllergy = v2.strAllergy
FOR XML PATH(''))
,1,2,'')
,AllergyRowNum = DENSE_RANK() OVER (PARTITION BY v1.strPatientFullName ORDER BY v1.strAllergy)
FROM
@VPatientPlusAllergyData v1
)
SELECT
strPatientFullName
,strAllergy1 = MAX(CASE WHEN AllergyRowNum = 1 THEN strAllergy END)
,strAllergy1Medications = MAX(CASE WHEN AllergyRowNum = 1 THEN strAllergyMedications END)
,strAllergy2 = MAX(CASE WHEN AllergyRowNum = 2 THEN strAllergy END)
,strAllergy2Medications = MAX(CASE WHEN AllergyRowNum = 2 THEN strAllergyMedications END)
FROM
cte
GROUP BY
strPatientFullName
在我准备和发布这篇文章时,@ZLK 写了一个很好的动态方法。