如何在 SQL Server 中以字符串形式获取详细表记录
Posted
技术标签:
【中文标题】如何在 SQL Server 中以字符串形式获取详细表记录【英文标题】:How to get detail table records as string in SQL Server 【发布时间】:2016-12-26 06:18:47 【问题描述】:我在数据库中有一个表,其中包含以下医院名称:
+------------+--------------+
| HospitalID | HospitalName |
+------------+--------------+
| 1 | Hosp1 |
| 2 | Hosp2 |
| 3 | Hosp3 |
| 4 | Hosp4 |
+------------+--------------+
还存在另一个表,其中包含以下活动名称:
+------------+--------------+
| ActivityID | ActivityName |
+------------+--------------+
| 1 | Act1 |
| 2 | Act2 |
| 3 | Act3 |
| 4 | Act4 |
| 5 | Act5 |
+------------+--------------+
这些表之间存在 N*M 关系,即每家医院可以开展不同的活动。因此需要另一个表,如下所示:
+----+------------+------------+
| ID | HospitalID | ActivityID |
+----+------------+------------+
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 1 | 5 |
| 4 | 2 | 1 |
| 5 | 2 | 3 |
| 6 | 3 | 2 |
+----+------------+------------+
我想编写一个 select 语句,在字符串字段中选择医院名称及其相关活动,如下所示:
+--------------+------------------+
| HospitalName | ActivityNames |
+--------------+------------------+
| Hosp1 | Act1, Act2, Act5 |
| Hosp2 | Act1, Act3 |
| Hosp3 | Act2 |
| Hosp4 | |
+--------------+------------------+
我已经使用游标为 ActivityNames 字段的函数编写了 select 语句,但它没有优化,并且系统性能随着记录数量的增加而降低。
关于如何解决这个问题的任何解决方案或建议?
【问题讨论】:
第一个建议 - 升级你的数据库。 Microsoft 不再支持 Sql server 2005。 【参考方案1】:您只需通过选择即可完成此操作。不需要循环或光标。循环会导致性能下降。
所以架构将是
CREATE TABLE #HOSPITAL( HOSPITALID INT, HOSPITALNAME VARCHAR(20))
INSERT INTO #HOSPITAL
SELECT 1, 'HOSP1'
UNION ALL
SELECT 2 , 'HOSP2'
UNION ALL
SELECT 3 ,'HOSP3'
UNION ALL
SELECT 4 , 'HOSP4'
CREATE TABLE #ACTIVITY( ActivityID INT, ActivityName VARCHAR(50) )
INSERT INTO #ACTIVITY
SELECT 1, 'Act1'
UNION ALL
SELECT 2, 'Act2'
UNION ALL
SELECT 3, 'Act3'
UNION ALL
SELECT 4, 'Act4'
UNION ALL
SELECT 5, 'Act5'
CREATE TABLE #HOSPITAL_ACT_MAP(ID INT, HospitalID INT, ActivityID INT)
INSERT INTO #HOSPITAL_ACT_MAP
SELECT 1, 1, 1
UNION ALL
SELECT 2, 1, 2
UNION ALL
SELECT 3, 1, 5
UNION ALL
SELECT 4, 2, 1
UNION ALL
SELECT 5, 2, 3
UNION ALL
SELECT 6, 3, 2
然后使用 CTE 进行如下选择
;WITH CTE AS (
SELECT DISTINCT H.HOSPITALNAME, A.ActivityName FROM #HOSPITAL_ACT_MAP HA
INNER JOIN #HOSPITAL H ON HA.HospitalID = H.HOSPITALID
INNER JOIN #ACTIVITY A ON HA.ActivityID = A.ActivityID
)
SELECT HOSPITALNAME
, (SELECT STUFF((SELECT ','+ActivityName FROM CTE C1
WHERE C1.HOSPITALNAME = C.HOSPITALNAME
FOR XML PATH('')),1,1,''))
FROM CTE C
GROUP BY HOSPITALNAME
从评论编辑
如果您不能使用CTE
和Stuff
,请选择方法2
DECLARE @TAB TABLE (HOSPITALNAME VARCHAR(20),ActivityName VARCHAR(20) )
INSERT INTO @TAB
SELECT DISTINCT H.HOSPITALNAME, A.ActivityName FROM #HOSPITAL_ACT_MAP HA
INNER JOIN #HOSPITAL H ON HA.HospitalID = H.HOSPITALID
INNER JOIN #ACTIVITY A ON HA.ActivityID = A.ActivityID
SELECT HOSPITALNAME, SUBSTRING(ACTIVITIES,1, LEN(ACTIVITIES)-1) FROM(
SELECT DISTINCT HOSPITALNAME,(SELECT ActivityName+',' FROM @TAB T1
WHERE T1.HOSPITALNAME = T.HOSPITALNAME
FOR XML PATH('') ) AS ACTIVITIES FROM @TAB T
)A
注意:出于性能目的,我将中间结果存储在@TAB(表变量)上。如果你愿意,你可以直接用 Sub Query 查询它。
【讨论】:
不确定 sql server 2005 是否支持stuff
,很确定它不支持 cte。但我似乎记得它确实支持for xml
...
使用 CTE 和 STUFF 的书面方法在 SQL Server 2005 和 2008 中对我来说都很好。感谢您宝贵的 cmets。
@shahram Akbarinasaji,您使用了内部联接。那么您如何获得“HOSP4”值。它与活动和映射无关。
@ShakeerMirza,您使用了内部连接。那么您如何获得“HOSP4”值。它与活动和映射无关。
@Mansoor ,将 using STUFF function to achieve your result :
CREATE TABLE #Hospital(HospitalID INT,HospitalName VARCHAR(100))
CREATE TABLE #Activity(ActivityID INT,ActivityName VARCHAR(100))
CREATE TABLE #RelationShip(Id INT,HospId INT,ActId INT)
CREATE TABLE #ConCat(HospitalID INT ,HospName VARCHAR(100), ActName
VARCHAR(100),UpFlag TINYINT DEFAULT(0))
DECLARE @HospId INT = 0,@String VARCHAR(200) = ''
INSERT INTO #Hospital(HospitalID ,HospitalName )
SELECT 1,'Hosp1' UNION ALL
SELECT 2,'Hosp2' UNION ALL
SELECT 3,'Hosp3' UNION ALL
SELECT 4,'Hosp4'
INSERT INTO #Activity(ActivityID ,ActivityName )
SELECT 1,'Act1' UNION ALL
SELECT 2,'Act2' UNION ALL
SELECT 3,'Act3' UNION ALL
SELECT 4,'Act4' UNION ALL
SELECT 5,'Act5'
INSERT INTO #RelationShip(ID,HospId,ActId)
SELECT 1 , 1 , 1 UNION ALL
SELECT 2 , 1 , 2 UNION ALL
SELECT 3 , 1 , 5 UNION ALL
SELECT 4 , 2 , 1 UNION ALL
SELECT 5 , 2 , 3 UNION ALL
SELECT 6 , 3 , 2
SELECT HospitalName , STUFF( ( SELECT ',' + ActivityName FROM #Activity
JOIN #RelationShip ON ActId = ActivityID WHERE HospId = HospitalID FOR XML
PATH('') ),1,1,'')
FROM #Hospital
GROUP BY HospitalID,HospitalName
***FOR SQLServer2005 Use below code***
INSERT INTO #ConCat (HospitalID ,HospName)
SELECT DISTINCT HospitalID ,HospitalName
FROM #Hospital
WHILE EXISTS(SELECT 1 FROM #ConCat WHERE UpFlag = 0)
BEGIN
SELECT @HospId = HospitalID FROM #ConCat WHERE UpFlag = 0 ORDER BY
HospitalID
SET @String = ''
SELECT @String = ISNULL(@String,'') + CAST(A.ActivityName AS VARCHAR) +
',' FROM
(
SELECT ActivityName
FROM #RelationShip
JOIN #Activity ON ActId = ActivityID
WHERE HospId = @HospId
) A
UPDATE #ConCat SET UpFlag = 1,ActName = CASE WHEN @String = '' THEN
@String ELSE SUBSTRING(@String,0,LEN(@String) ) END WHERE HospitalID
= @HospId
END
SELECT * FROM #ConCat
【讨论】:
不确定 sql server 2005 是否支持stuff
,但我似乎记得它确实支持for xml
...
@ZoharPeled 2005 支持的东西
@t-clausen.dk 很高兴知道,如果我能发明那台时光机 :-)
但是在这个微软文档中声明它从 2008 年开始支持 msdn.microsoft.com/en-us/library/ms188043.aspx@t-clausen.dk
@ShakeerMirza 认为你是对的。我正在使用这个link【参考方案3】:
您可以使用 json 来提高前端的性能。 如果您使用的是开源数据库,则没有特定的解决方案。
尝试使用 IBM db2 或 ORACLE 数据库来确保您的应用程序的性能。
然后生成json数据。你会发现速度的提升
【讨论】:
Faruk 他已经标记了Sqlserver,所以在此基础上给出答案。 我使用 SQL Server 2005 作为数据库服务器。我的问题是如何获取 ActivityNames 字段。您介意提供更详细的信息吗?以上是关于如何在 SQL Server 中以字符串形式获取详细表记录的主要内容,如果未能解决你的问题,请参考以下文章
如何在 SQL Server Management Studio 中以“真实”CSV 格式获取导出输出?
如何从索引中以字符串形式获取突出显示数据或如何将其打印出来?