SQL Server несколько ЗАМЕНИТЬ с помощью таблицы #temp
Я пытаюсь ЗАМЕНИТЬ несколько символов в запросе SQL Server и хочу достичь этого с помощью таблицы #temp вместо вложенного REPLACE. Я получил код SQL ниже и хочу достичь результата, например
ABC
DEF
GHI
IF OBJECT_ID('tempdb..#temp') IS NOT NULL DROP TABLE #temp
IF OBJECT_ID('tempdb..#temp2') IS NOT NULL DROP TABLE #temp2
CREATE TABLE #temp
(
STRING_TO_REPLACE NVARCHAR(5)
)
INSERT INTO #temp (STRING_TO_REPLACE)
VALUES (' ')
,('/')
,('_')
CREATE TABLE #temp2
(
STRING_NAME NVARCHAR(5)
)
INSERT INTO #temp2 (STRING_NAME)
VALUES ('A BC')
,('D/EF')
,('G_HI')
SELECT REPLACE(t2.STRING_NAME,(SELECT t1.STRING_TO_REPLACE
FROM #temp t1),'')
FROM #temp2 t2
IF OBJECT_ID('tempdb..#temp') IS NOT NULL DROP TABLE #temp
IF OBJECT_ID('tempdb..#temp2') IS NOT NULL DROP TABLE #temp2
Я могу добиться результата с вложенной заменой
SELECT REPLACE(REPLACE(REPLACE(t2.STRING_NAME,'_',''),'/',''),' ','') FROM #temp2 t2
но очень хотелось бы сделать это через таблицу #temp. Пожалуйста, помогите мне в этом.
Когда я пытаюсь запустить свой первый код, я получаю следующую ошибку:
Msg 512, уровень 16, состояние 1, строка 23 Подзапрос возвращает более 1 стоимость. Это недопустимо, когда подзапрос следует =,! =, <, < =,
>= или когда подзапрос используется как выражение.
Ответы
Ответ 1
Вот один из способов: CROSS APPLY
SELECT result
FROM #temp2 t2
CROSS apply (SELECT Replace(string_name, t1.string_to_replace, '') AS
result
FROM #temp t1) cs
WHERE result <> string_name
Результат:
result
-----
ABC
DEF
GHI
Примечание: Это будет работать, только если каждый string_name
имеет только один string_to_replace
Обновление: Чтобы обрабатывать более одного string_to_replace
в одном string_name
, это один из способов: Dynamic sql
Я сделал небольшое изменение в таблице #temp
, добавив свойство identity
в цикл
IF Object_id('tempdb..#temp') IS NOT NULL
DROP TABLE #temp
IF Object_id('tempdb..#temp2') IS NOT NULL
DROP TABLE #temp2
CREATE TABLE #temp
(
id INT IDENTITY(1, 1),
string_to_replace NVARCHAR(5)
)
INSERT INTO #temp
(string_to_replace)
VALUES (' '),
('/'),
('_')
CREATE TABLE #temp2
(
string_name NVARCHAR(5)
)
INSERT INTO #temp2
(string_name)
VALUES ('A BC'),
('D/EF'),
('G_HI'),
('A BD_')
DECLARE @col_list VARCHAR(8000)= '',
@sql VARCHAR(max),
@cntr INT,
@inr INT =1,
@STRING_TO_REPLACE NVARCHAR(5)
SELECT @cntr = Max(id)
FROM #temp
SET @sql = 'select '
WHILE @inr < = @cntr
BEGIN
SELECT @STRING_TO_REPLACE = string_to_replace
FROM #temp
WHERE id = @inr
IF @inr = 1
SET @col_list = 'replace (STRING_NAME,'''
+ @STRING_TO_REPLACE + ''','''')'
ELSE
SET @col_list = 'replace (' + @col_list + ','''
+ @STRING_TO_REPLACE + ''','''')'
SET @inr+=1
END
SET @sql += ' from #temp2'
--print @col_list
SET @sql = 'select ' + @col_list + ' as Result from #temp2'
--print @sql
EXEC (@sql)
Результат:
Result
------
ABC
DEF
GHI
ABD
Ответ 2
Многократная замена может быть достигнута с помощью рекурсивного CTE в следующем примере:
IF OBJECT_ID('tempdb..#temp') IS NOT NULL DROP TABLE #temp
IF OBJECT_ID('tempdb..#temp2') IS NOT NULL DROP TABLE #temp2
CREATE TABLE #temp
(
STRING_TO_REPLACE NVARCHAR(10)
,Pattern NVARCHAR(10)
)
INSERT INTO #temp (STRING_TO_REPLACE, Pattern)
VALUES (' ', '% %')
,('/', '%/%')
,('_', '%[_]%') ;
CREATE TABLE #temp2
(
STRING_NAME NVARCHAR(10)
);
INSERT INTO #temp2 (STRING_NAME)
VALUES ('A BC')
,('D/EF_F E')
,('G_HI')
,('XYZ');
WITH CTE_Replace AS
(
SELECT STRING_NAME AS OriginalString
,CAST(STRING_NAME AS NVARCHAR(10)) AS ReplacedString
,CAST('' AS NVARCHAR(10)) AS StringToReplace
,1 AS ReplaceCount
FROM #temp2 ancor
UNION ALL
SELECT CTE_Replace.OriginalString
,CAST(REPLACE(CTE_Replace.ReplacedString, rep.STRING_TO_REPLACE, '') AS NVARCHAR(10)) AS ReplacedString
,CAST(rep.STRING_TO_REPLACE AS NVARCHAR(10)) AS StringToReplace
,CTE_Replace.ReplaceCount + 1 AS ReplaceCount
FROM #temp rep
INNER JOIN CTE_Replace ON CTE_Replace.ReplacedString LIKE rep.Pattern
)
,CTE_FinalReplacedString AS
(
SELECT OriginalString
,ReplacedString
,ReplaceCount
,ROW_NUMBER() OVER (PARTITION BY OriginalString ORDER BY ReplaceCount DESC) AS [Rank]
FROM CTE_Replace
)
SELECT *
FROM CTE_FinalReplacedString
WHERE [Rank] = 1
Обратите внимание, что таблица #temp
была обновлена, чтобы включить дополнительный столбец с именем Pattern
, этот столбец содержит шаблон поиска, который будет использоваться для поиска конкретных строк, которые необходимо заменить. Это также было сделано для упрощения утверждения объединения в рекурсивном CTE. Также обратите внимание, что для поиска символа _
шаблон поиска должен был быть обновлен как '%[_]%'
. Причина этого в том, что SQL Server интерпретирует символ _
как дикий символ вместо определенного символа, который мы пытаемся найти.
Ответ 3
заменить в таблице, вероятно, проще здесь
update t2
set t2.string_name = Replace(t2.string_name, t1.string_to_replace, '')
from #temp2 t2
cross join #temp1 t1
Ответ 4
Другой способ с рекурсивным CTE (полная партия ниже):
--Create a sample table, you should use YourTable
CREATE TABLE #temp2 (
STRING_NAME NVARCHAR(max)
)
INSERT INTO #temp2 (STRING_NAME)
VALUES ('A BC'),('D/EF'),('G_HI'),('J_K/L_'),('MNO')
--I add some more objects here
Основной запрос:
;WITH replacement AS (
SELECT *
FROM (VALUES (' '),('/'),('_')
) as t(STRING_TO_REPLACE)
), cte AS (
SELECT STRING_NAME,
STRING_NAME as OriginalString,
ROW_NUMBER() OVER (ORDER BY STRING_NAME) as rn,
1 as [Level]
FROM #temp2 t2
UNION ALL
SELECT REPLACE(c.STRING_NAME,t.STRING_TO_REPLACE,'~'),
c.OriginalString,
c.rn,
[Level]+1
FROM cte c
INNER JOIN replacement t
ON CHARINDEX(t.STRING_TO_REPLACE,c.STRING_NAME,0) > 0
)
SELECT TOP 1 WITH TIES OriginalString,
STRING_NAME
FROM cte
ORDER BY ROW_NUMBER() OVER (PARTITION BY rn ORDER BY [Level] DESC)
OPTION (MAXRECURSION 0)
Вывод:
OriginalString STRING_NAME
A BC A~BC
D/EF D~EF
J_K/L_ J~K~L~
G_HI G~HI
MNO MNO
Ответ 5
Простым способом справиться с этим является загрузка копии PatExclude8K, функции T-SQL, предназначенной именно для этого типа задач. Вот несколько примеров:
-- remove all non-aplphabetical characters
SELECT NewString FROM #temp2 CROSS APPLY dbo.PatExclude8K(STRING_NAME,'[^A-Z]');
-- remove all spaces, forward slashes and underscores
SELECT NewString FROM #temp2 CROSS APPLY dbo.PatExclude8K(STRING_NAME,'[ /_]');
Оба запроса создают этот результирующий набор:
NewString
------------
ABC
DEF
GHI
Ответ 6
Я нашел ниже код в stackoverflow, который кажется более близким к тому, что я пытаюсь достичь, но боюсь, что как я могу использовать его с моим кодом
declare @String varchar(max) = '(N_100-(6858)*(6858)*N_100/0_2)%N_35'
--table containing values to be replaced
create table #Replace
(
StringToReplace varchar(100) not null primary key clustered
,ReplacementString varchar(100) not null
)
insert into #Replace (StringToReplace, ReplacementString)
values ('+', '~')
,('-', '~')
,('*', '~')
,('/', '~')
,('%', '~')
,('(', '~')
,(')', '~')
select @String = replace(@String, StringToReplace, ReplacementString)
from #Replace a
select @String
drop table #Replace
ИЗМЕНИТЬ по gofr1
CREATE FUNCTION replacement
(
@String nvarchar(max)
)
RETURNS nvarchar(max)
AS
BEGIN
DECLARE @Replace TABLE (
StringToReplace nvarchar(100),
ReplacementString nvarchar(100)
)
INSERT INTO @Replace (StringToReplace, ReplacementString)
VALUES ('+', '~')
,('-', '~')
,('*', '~')
,('/', '~')
,('%', '~')
,('(', '~')
,(')', '~')
SELECT @String = replace(@String, StringToReplace, ReplacementString)
FROM @Replace
RETURN @String
END
GO
Затем назовите его:
SELECT dbo.replacement ('A B-C/d')
Вывод:
A B~C~d