仅当 table 中不存在该值时才更新 SQL 列
Update SQL column only if the value doesn't already exist in the table
我正在使用 SQL Server 2012 并且我的 table 的列有逗号分隔值(不好的做法,但现在不可避免)。每次用户尝试添加申请人时,我如何检查它是否已经存在,并且只有在它不存在时才更新(追加)。
JobID Applicants
----------------------------
J001 a001,a002,a003
例如。如果我尝试一次性添加求职者 a003、a004 以求职 J001,则只应添加 a004。
我相信合并在这里会有所帮助,但我无法弄清楚如何。
我尝试通过转换为 XML 来拆分传入值(通过 USP 参数),但现在不知道在存储过程中要做什么。
您已经知道这是一个非常糟糕的数据结构,因此我们不必深入探讨。当你卡在这样的结构中时,你可以使用字符串操作,但效率不是特别高;
我的建议是一次添加一个:
update t
set Applicants = (case when Applicants is not null
then Applicants + ',' + cast(@ApplicantId as varchar(255)
else cast(@ApplicantId)
end)
where JobId = @JobId and
',' + Applicants + ',' not like '%,' + cast(@ApplicantId as varchar(255)) + '%,';
没有必要通过尝试将更新值和存储值一起用于逗号分隔列表来解决您的问题。
而且,让我再说一遍,以这种方式存储值在 SQL 中是一个非常非常糟糕的主意,只有在其他人(不了解情况)强加这种情况时才应支持。
拆分 a003、a004 并使用逗号(例如 'a003'||',')将每个字符串搜索到有条件的申请者字段中如何?
希望你觉得有用。
好的,所以你知道它不好 Gordon 指出它很糟糕,他给了你一次更新 1 行的方法,但是如果你正在处理一个真正应该代表 table 我猜你不想一次只更新 1 条记录...
首先,如果您有这样的数据,首先要为自己准备一个好的 table 值函数来拆分定界字符串!这是我刚写的一个,如果你想要一个多字符定界符但不太灵活但给你和想法。
CREATE FUNCTION dbo.SplitDelimitedString
(
@DelimitedString VARCHAR(8000)
,@Delimitor CHAR(1)
)
RETURNS
@ReturnTable TABLE (Id INT,String VARCHAR(8000))
AS
BEGIN
IF CHARINDEX(@Delimitor,@DelimitedString) = 0
BEGIN
INSERT INTO @ReturnTable (Id,String)
VALUES (1,@DelimitedString)
RETURN
END
;WITH cteStrings AS (
SELECT
1 AS Id
,LEFT(@DelimitedString,CHARINDEX(@Delimitor,@DelimitedString) - 1) as String
,RIGHT(@DelimitedString,LEN(@DelimitedString) - CHARINDEX(@Delimitor,@DelimitedString))
+ CASE WHEN RIGHT(@DelimitedString,1) <> @Delimitor THEN @Delimitor ELSE '' END as RemainingStrings
UNION ALL
SELECT
Id + 1 as Id
,LEFT(RemainingStrings,CHARINDEX(@Delimitor,RemainingStrings)-1) as String
,RIGHT(RemainingStrings,LEN(RemainingStrings) - CHARINDEX(@Delimitor,RemainingStrings)) as RemainingStrings
FROM
cteStrings
WHERE
CHARINDEX(@Delimitor,RemainingStrings) > 0
)
INSERT INTO @ReturnTable (Id, String)
SELECT
Id
,String
FROM
cteStrings
RETURN
END
GO
接下来确保您知道如何重新连接拆分的字符串,有很多示例和技术我将使用 STUFF with FOR XML PATH 技术。拆分要更新的字符串后,您也可以使用相同的函数通过交叉应用拆分 Table 或使用 Gordon 的 LIKE 技术。
这是一个使用戈登技术的例子
DECLARE @TableA AS TABLE (JobId CHAR(4), Applicants VARCHAR(100))
INSERT INTO @TableA (JobId, Applicants) VALUES ('J001','a001,a002,a003')
DECLARE @AddApplicants VARCHAR(100) = 'a003,a004,a005,a005'
DECLARE @JobId CHAR(5) = 'J001'
SET @AddApplicants = STUFF(
(SELECT DISTINCT ',' + String
FROM
dbo.SplitDelimitedString(@AddApplicants,',') a
LEFT JOIN @TableA t
ON t.JobId = @JobId
AND t.Applicants LIKE '%,' + a.String + '%'
WHERE
t.JobId IS NULL
FOR XML PATH(''))
,1,1,'')
UPDATE @TableA
SET Applicants = CASE WHEN LEN(Applicants) > 0 THEN Applicants + ',' ELSE '' END + @AddApplicants
WHERE
JobId = @JobId
SELECT *
FROM
@TableA
如果您更愿意使用 CROSS APPLY,您可以这样做:
SET @AddApplicants = STUFF(
(SELECT DISTINCT ',' + a.String
FROM
dbo.SplitDelimitedString(@AddApplicants,',') a
LEFT JOIN (
SELECT DISTINCT a.String
FROM
@TableA t
CROSS APPLY dbo.SplitDelimitedString(t.Applicants,',') a
WHERE
t.JobId = @JobId
) t
ON a.String = t.String
WHERE
t.String IS NULL
FOR XML PATH(''))
,1,1,'')
这是您问题的答案。
按照以下步骤操作。
1。创建以下函数。
CREATE FUNCTION SplitString
(
@Input NVARCHAR(MAX),
@Character CHAR(1)
)
RETURNS @Output TABLE (
Item NVARCHAR(1000)
)
AS
BEGIN
DECLARE @StartIndex INT, @EndIndex INT
SET @StartIndex = 1
IF SUBSTRING(@Input, LEN(@Input) - 1, LEN(@Input)) <> @Character
BEGIN
SET @Input = @Input + @Character
END
WHILE CHARINDEX(@Character, @Input) > 0
BEGIN
SET @EndIndex = CHARINDEX(@Character, @Input)
INSERT INTO @Output(Item)
SELECT SUBSTRING(@Input, @StartIndex, @EndIndex - 1)
SET @Input = SUBSTRING(@Input, @EndIndex + 1, LEN(@Input))
END
RETURN
END
GO
创建样本输入table #T
SELECT 'J001' as JobID, CONVERT(VARCHAR(50),'a001,a002,a003') as Applicants INTO #T
下面的脚本将执行您的更新。
;with cte_1
as
(SELECT item FROM #T
cross apply (select item from dbo.SplitString(Applicants, ','))X
WHERE JobID='J001')
UPDATE t
set t.Applicants= t.Applicants+','+ STUFF(( SELECT ','+b.Item
FROM dbo.SplitString('a001,a002,a004,a005', ',') b
LEFT JOIN cte_1 a ON b.Item=a.Item
WHERE a.Item IS NULL FOR XML PATH(''), TYPE).value('.','VARCHAR(max)'), 1, 1, '')
FROM #T t WHERE t.JobID='J001'
我正在使用 SQL Server 2012 并且我的 table 的列有逗号分隔值(不好的做法,但现在不可避免)。每次用户尝试添加申请人时,我如何检查它是否已经存在,并且只有在它不存在时才更新(追加)。
JobID Applicants
----------------------------
J001 a001,a002,a003
例如。如果我尝试一次性添加求职者 a003、a004 以求职 J001,则只应添加 a004。
我相信合并在这里会有所帮助,但我无法弄清楚如何。
我尝试通过转换为 XML 来拆分传入值(通过 USP 参数),但现在不知道在存储过程中要做什么。
您已经知道这是一个非常糟糕的数据结构,因此我们不必深入探讨。当你卡在这样的结构中时,你可以使用字符串操作,但效率不是特别高;
我的建议是一次添加一个:
update t
set Applicants = (case when Applicants is not null
then Applicants + ',' + cast(@ApplicantId as varchar(255)
else cast(@ApplicantId)
end)
where JobId = @JobId and
',' + Applicants + ',' not like '%,' + cast(@ApplicantId as varchar(255)) + '%,';
没有必要通过尝试将更新值和存储值一起用于逗号分隔列表来解决您的问题。
而且,让我再说一遍,以这种方式存储值在 SQL 中是一个非常非常糟糕的主意,只有在其他人(不了解情况)强加这种情况时才应支持。
拆分 a003、a004 并使用逗号(例如 'a003'||',')将每个字符串搜索到有条件的申请者字段中如何?
希望你觉得有用。
好的,所以你知道它不好 Gordon 指出它很糟糕,他给了你一次更新 1 行的方法,但是如果你正在处理一个真正应该代表 table 我猜你不想一次只更新 1 条记录...
首先,如果您有这样的数据,首先要为自己准备一个好的 table 值函数来拆分定界字符串!这是我刚写的一个,如果你想要一个多字符定界符但不太灵活但给你和想法。
CREATE FUNCTION dbo.SplitDelimitedString
(
@DelimitedString VARCHAR(8000)
,@Delimitor CHAR(1)
)
RETURNS
@ReturnTable TABLE (Id INT,String VARCHAR(8000))
AS
BEGIN
IF CHARINDEX(@Delimitor,@DelimitedString) = 0
BEGIN
INSERT INTO @ReturnTable (Id,String)
VALUES (1,@DelimitedString)
RETURN
END
;WITH cteStrings AS (
SELECT
1 AS Id
,LEFT(@DelimitedString,CHARINDEX(@Delimitor,@DelimitedString) - 1) as String
,RIGHT(@DelimitedString,LEN(@DelimitedString) - CHARINDEX(@Delimitor,@DelimitedString))
+ CASE WHEN RIGHT(@DelimitedString,1) <> @Delimitor THEN @Delimitor ELSE '' END as RemainingStrings
UNION ALL
SELECT
Id + 1 as Id
,LEFT(RemainingStrings,CHARINDEX(@Delimitor,RemainingStrings)-1) as String
,RIGHT(RemainingStrings,LEN(RemainingStrings) - CHARINDEX(@Delimitor,RemainingStrings)) as RemainingStrings
FROM
cteStrings
WHERE
CHARINDEX(@Delimitor,RemainingStrings) > 0
)
INSERT INTO @ReturnTable (Id, String)
SELECT
Id
,String
FROM
cteStrings
RETURN
END
GO
接下来确保您知道如何重新连接拆分的字符串,有很多示例和技术我将使用 STUFF with FOR XML PATH 技术。拆分要更新的字符串后,您也可以使用相同的函数通过交叉应用拆分 Table 或使用 Gordon 的 LIKE 技术。
这是一个使用戈登技术的例子
DECLARE @TableA AS TABLE (JobId CHAR(4), Applicants VARCHAR(100))
INSERT INTO @TableA (JobId, Applicants) VALUES ('J001','a001,a002,a003')
DECLARE @AddApplicants VARCHAR(100) = 'a003,a004,a005,a005'
DECLARE @JobId CHAR(5) = 'J001'
SET @AddApplicants = STUFF(
(SELECT DISTINCT ',' + String
FROM
dbo.SplitDelimitedString(@AddApplicants,',') a
LEFT JOIN @TableA t
ON t.JobId = @JobId
AND t.Applicants LIKE '%,' + a.String + '%'
WHERE
t.JobId IS NULL
FOR XML PATH(''))
,1,1,'')
UPDATE @TableA
SET Applicants = CASE WHEN LEN(Applicants) > 0 THEN Applicants + ',' ELSE '' END + @AddApplicants
WHERE
JobId = @JobId
SELECT *
FROM
@TableA
如果您更愿意使用 CROSS APPLY,您可以这样做:
SET @AddApplicants = STUFF(
(SELECT DISTINCT ',' + a.String
FROM
dbo.SplitDelimitedString(@AddApplicants,',') a
LEFT JOIN (
SELECT DISTINCT a.String
FROM
@TableA t
CROSS APPLY dbo.SplitDelimitedString(t.Applicants,',') a
WHERE
t.JobId = @JobId
) t
ON a.String = t.String
WHERE
t.String IS NULL
FOR XML PATH(''))
,1,1,'')
这是您问题的答案。
按照以下步骤操作。
1。创建以下函数。
CREATE FUNCTION SplitString
(
@Input NVARCHAR(MAX),
@Character CHAR(1)
)
RETURNS @Output TABLE (
Item NVARCHAR(1000)
)
AS
BEGIN
DECLARE @StartIndex INT, @EndIndex INT
SET @StartIndex = 1
IF SUBSTRING(@Input, LEN(@Input) - 1, LEN(@Input)) <> @Character
BEGIN
SET @Input = @Input + @Character
END
WHILE CHARINDEX(@Character, @Input) > 0
BEGIN
SET @EndIndex = CHARINDEX(@Character, @Input)
INSERT INTO @Output(Item)
SELECT SUBSTRING(@Input, @StartIndex, @EndIndex - 1)
SET @Input = SUBSTRING(@Input, @EndIndex + 1, LEN(@Input))
END
RETURN
END
GO
创建样本输入table #T
SELECT 'J001' as JobID, CONVERT(VARCHAR(50),'a001,a002,a003') as Applicants INTO #T
下面的脚本将执行您的更新。
;with cte_1 as (SELECT item FROM #T cross apply (select item from dbo.SplitString(Applicants, ','))X WHERE JobID='J001') UPDATE t set t.Applicants= t.Applicants+','+ STUFF(( SELECT ','+b.Item FROM dbo.SplitString('a001,a002,a004,a005', ',') b LEFT JOIN cte_1 a ON b.Item=a.Item WHERE a.Item IS NULL FOR XML PATH(''), TYPE).value('.','VARCHAR(max)'), 1, 1, '') FROM #T t WHERE t.JobID='J001'