我有两个表affectedservice
和call
受影响的服务是这样构建的;
ID Name
1 Area 1
2 Area 2
并且打电话就像这样
ID AffectedService
100 1
101 1,2
102 2
如何从通话ID中选择受影响的服务名称?
我试过
select Aff_Serv_Name
from AffectedService
where Aff_Serv_ID in (
select Spec_Area_ID
from call
where call_id = 100)
但是出现了这个错误
Conversion failed when converting the nvarchar value '1,2' to data type int.
The AffectedService'列的范围可以是1到4个数字
修改 我知道将CSV数据存储在列中是非常糟糕的,但不幸的是它不是我的数据库,并且数据库大约有20年的历史,所以它无法更改 SQL Server 2012数据库
在sql fiddle http://sqlfiddle.com/#!6/d548e
上构建架构答案 0 :(得分:1)
动态SQL可能是最好的方法,任何字符串都可以像执行SQL代码一样执行,这使您可以将SQL命令创建为字符串,这非常强大。我在这里用过这个
DECLARE @Call_ID INT = 100
DECLARE @IdList VARCHAR(100)
DECLARE @SQLQuery AS NVARCHAR(500)
SET @IdList = (SELECT Spec_Area_ID
FROM Call
WHERE ID = @Call_ID)
SET @SQLQuery = 'SELECT STUFF((SELECT '', '' + Aff_Serv_Name
FROM AffectedService
WHERE Aff_Serv_ID IN (' + @IdList + ')
FOR XML PATH('''')), 1, 1, '''') [Specific_Area]'
EXECUTE(@SQLQuery)
以此方式填充字符串变量(实际上是您的服务列表),然后将其放入SELECT...WHERE...IN()
中,您已经证明自己在问题中知道。我还通过使用FOR XML PATH()
函数使其返回到适合您问题的CSV列表,允许您将所有返回值放在同一行上,并用逗号分隔每个值,但是在第一个值之前放置一个逗号,我使用stuff函数将其清除。
答案 1 :(得分:0)
嗯,你不能使用IN
,因为IN
需要一个值列表,而你实际上提供的是一个值(包含一个列表)。
由于您在评论中写道无法更改数据库结构,因此您可以使用string splitting user defined function,也可以使用LIKE
:
select Aff_Serv_Name
from AffectedService
inner join call on '%,'+ CAST(Aff_Serv_ID as varchar(10)) +',%' LIKE ','+ Spec_Area_ID +','
where call_id = 100
我已将您的查询更改为内部联接,因为它更具可读性 - 当然您也可以使用子查询版本:
select Aff_Serv_Name
from AffectedService
where '%,'+ CAST(Aff_Serv_ID as varchar(10)) +',%' LIKE ','+
(
select Spec_Area_ID
from call
where call_id = 100
) +','
答案 2 :(得分:0)
您还可以拆分和透视数据,使其更加可行,并以更正常的方式显示。关系模型。不是很简洁:
IF OBJECT_ID('dbo.mytableService', 'U') IS NOT NULL
DROP TABLE dbo.mytableService;
IF OBJECT_ID('dbo.mytableCall', 'U') IS NOT NULL
DROP TABLE dbo.mytableCall;
CREATE TABLE mytableService(ID INT ,AffectedService VARCHAR (100))
CREATE TABLE mytableCall(ID INT, Name VARCHAR (100))
--test data
INSERT INTO mytableService (ID, AffectedService)
VALUES (100 , '1'),
(101 ,'1,2'),
(102 ,'2')
INSERT INTO mytableCall (ID, Name)
VALUES
(1,'Area 1'),
(2,'Area 2'),
(3,'Area 3')
--table for split and pivot
DECLARE @table table ( ID INT,Service VARCHAR(200))
INSERT INTO @table(ID,Service) SELECT ID,AffectedService from mytableService
;WITH Split_Names (ID,Service, xmlService)
AS
(
SELECT
ID,
Service,
CONVERT(XML,'<Names><name>'
+ REPLACE(Service,',', '</name><name>') + '</name></Names>') AS xmlService
FROM @table
)
,Results
AS
( SELECT
ID,
xmlService.value('/Names[1]/name[1]','varchar(100)') AS Service1
,xmlService.value('/Names[1]/name[2]','varchar(100)') AS Service2
,xmlService.value('/Names[1]/name[3]','varchar(100)') AS Service3
,xmlService.value('/Names[1]/name[4]','varchar(100)') AS Service4
FROM Split_Names)
,
Atomic_Services
AS
(
SELECT ROW_NUMBER() OVER(ORDER BY ID) AS RN, ID,Services FROM Results
UNPIVOT
(Services FOR Service IN
(Service1,Service2,Service3,Service4 )
)AS unpvt
)
--join
SELECT *
FROM Atomic_Services cte
JOIN mytableCall area
ON cte.Services = area.ID