我有一栏保存纯文本,并使用定界符将第一个表中的图像(位于base64中)替换为由一些唯一字符和base64表ID组成的定界符。
例如:
TableA
notesColumn
图片在这里<## 1 ##>,然后是一些文本和另一张图片<## 2 ##>,在这里结束
每个实例可以返回多个结果。具有1个定界符的行可以正常工作。这里的问题是,当我尝试根据整个页面选择要显示的带有多个定界符的分隔符时,它将显示类似的内容,并显示为多行。
图片就在这里data:image / png; base64 ...然后是一些文字和另一张图片<## 2 ##>这是结尾
图片在这里<## 1 ##>,然后是一些文本和另一个图片数据:image / png; base64 ...,这是结尾
我的一般查询是
SELECT REPLACE(A.notesColumn,'<##'+CAST(B.base64ID AS VARCHAR(25))+'##>', B.docImage) [noteText]
FROM tableA A
LEFT JOIN base64Table B ON A.ID = B.tableANote
WHERE pageID = @pageID
如何解决将其显示为多个结果而不是仅显示1行的问题,而不考虑分隔符有多少?
答案 0 :(得分:2)
您可以执行以下操作:使用字符串拆分功能将原始文本分解为组成词,然后join
根据需要放入base64Table
中以获取相关的替换值,然后通过任一方法重新组合FOR XML
或STRING_AGG
,具体取决于您的SQL Server是2017之前还是之后。
如果您使用的是SQL Server 2016或更高版本,则还可以使用STRING_SPLIT
,如果没有,则可以在此答案的末尾使用该功能,这是我对Jeff Moden's的修改版本。
declare @b table(id int,p varchar(100));
insert into @b values(1,'THIS IS PICTURE 1'),(2,'THIS IS PICTURE 2'),(3,'THIS IS PICTURE 3');
declare @t table(v varchar(500));
insert into @t values('A picture is right here <##1##> and then here is some text and another picture <##2##> and here is the end'),('Another picture is here <##1##> and yet more text and another picture <##2##> and here is the end');
select t.v as Original
,stuff((select ' ' + isnull(b.p,s.Item)
from dbo.fn_StringSplit4k(t.v,' ',null) as s
left join @b as b
on left(s.Item,3) = '<##'
and cast(substring(s.Item,4,1) as int) = b.id
order by s.rn
for xml path('')
),1,1,'') as Replaced
from @t as t;
+------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------+
| Original | Replaced |
+------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------+
| A picture is right here <##1##> and then here is some text and another picture <##2##> and here is the end | A picture is right here THIS IS PICTURE 1 and then here is some text and another picture THIS IS PICTURE 2 and here is the end |
| Another picture is here <##1##> and yet more text and another picture <##2##> and here is the end | Another picture is here THIS IS PICTURE 1 and yet more text and another picture THIS IS PICTURE 2 and here is the end |
+------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------+
create function dbo.fn_StringSplit4k
(
@str nvarchar(4000) = ' ' -- String to split.
,@delimiter as nvarchar(1) = ',' -- Delimiting value to split on.
,@num as int = null -- Which value to return.
)
returns table
as
return
-- Start tally table with 10 rows.
with n(n) as (select 1 union all select 1 union all select 1 union all select 1 union all select 1 union all select 1 union all select 1 union all select 1 union all select 1 union all select 1)
-- Select the same number of rows as characters in @str as incremental row numbers.
-- Cross joins increase exponentially to a max possible 10,000 rows to cover largest @str length.
,t(t) as (select top (select len(isnull(@str,'')) a) row_number() over (order by (select null)) from n n1,n n2,n n3,n n4)
-- Return the position of every value that follows the specified delimiter.
,s(s) as (select 1 union all select t+1 from t where substring(isnull(@str,''),t,1) = @delimiter)
-- Return the start and length of every value, to use in the SUBSTRING function.
-- ISNULL/NULLIF combo handles the last value where there is no delimiter at the end of the string.
,l(s,l) as (select s,isnull(nullif(charindex(@delimiter,isnull(@str,''),s),0)-s,4000) from s)
select rn
,item
from(select row_number() over(order by s) as rn
,substring(@str,s,l) as item
from l
) a
where rn = @num
or @num is null;
再次查看您的问题,您可能需要一个可处理4000个以上字符的字符串分隔符。如果是这种情况,您可以改用它,尽管它在较小的字符串上的性能可能会比4k版本差:
create function dbo.fn_StringSplitMax
(
@str nvarchar(max) = ' ' -- String to split.
,@delimiter as nvarchar(max) = ',' -- Delimiting value to split on.
,@num as int = null -- Which value to return.
)
returns table
as
return
with s as
( -- Convert the string to an XML value, replacing the delimiter with XML tags
select convert(xml,'<x>' + replace((select @str for xml path('')),@delimiter,'</x><x>') + '</x>').query('.') as s
)
select rn
,item -- Select the values from the generated XML value by CROSS APPLYing to the XML nodes
from(select row_number() over (order by (select null)) as rn
,n.x.value('.','nvarchar(max)') as item
from s
cross apply s.nodes('x') as n(x)
) a
where rn = @num
or @num is null;
答案 1 :(得分:0)
这是罕见的时刻之一,其中古怪的更新可以提供很多帮助:
(感谢@iamdave,他的mcve部分已使用)
CREATE TABLE B(id int,p varchar(100));
insert into B values(1,'THIS IS PICTURE 1'),(2,'THIS IS PICTURE 2'),(3,'THIS IS PICTURE 3');
GO
CREATE TABLE A(v varchar(500));
insert into A values('A picture is right here <##1##> and then here is some text and another picture <##2##> and here is the end')
,('Another picture is here <##1##> and yet more text and another picture <##2##> and here is the end')
,('Another example with a picutre <##2##> here and one more here <##3##>');
GO
-魔术发生在这里:
CREATE FUNCTION dbo.MultipleImageReplace(@input VARCHAR(MAX))
RETURNS VARCHAR(MAX) AS
BEGIN
SELECT @input = REPLACE(@input,CONCAT('<##',B.id,'##>'),B.p) FROM B;
RETURN @input;
END
GO
-这就是您的称呼方式:
SELECT *
,dbo.MultipleImageReplace(A.v) AS ReplacedText
FROM A;
GO
-清理:小心真实数据!
DROP FUNCTION dbo.MultipleImageReplace;
GO
DROP TABLE B;
GO
DROP TABLE A;
结果
A picture is right here THIS IS PICTURE 1 and then here is some text and another picture THIS IS PICTURE 2 and here is the end
Another picture is here THIS IS PICTURE 1 and yet more text and another picture THIS IS PICTURE 2 and here is the end
Another example with a picutre THIS IS PICTURE 2 here and one more here THIS IS PICTURE 3
神奇古怪的更新的某些背景:
该函数使用@input=Modify(@input)
。这将导致逐行操作,其中表B
的每一行将修改@input
的内容并将其重新分配给变量。因此,下一行将使用修改后的内容,依此类推。
此方法存在一些严重的缺点,一般不建议这样做。但是在这种情况下,排序无关紧要,则可以尝试一下。
答案 2 :(得分:0)
我将其作为第二个答案,因为方法与另一方法完全不同,因此我不想将其混淆。
您可以使用XQuery
及其强大的功能来处理通用数据。
测试场景:
CREATE TABLE B(id int,p varchar(100));
insert into B values(1,'THIS IS PICTURE 1'),(2,'THIS IS PICTURE 2'),(3,'THIS IS PICTURE 3');
GO
CREATE TABLE A(v varchar(500));
insert into A values('A picture is right here <##1##> and then here is some text and another picture <##2##> and here is the end')
,('Another picture is here <##1##> and yet more text and another picture <##2##> and here is the end')
,('Another example with a picutre <##2##> here and one more here <##3##>');
GO
-CTE将使用一些替代方法将您的字符串转换为XML
WITH Casted AS
(
SELECT *
,CAST('<root>
<row>
<text>' + REPLACE(REPLACE(A.v,'<##','</text><img index="'),'##>','"/><text>') + '</text>
</row>
<images>' +
--and we will include the pictures to be part of the XML
--this will make it easier to use them in the XQuery
--You might use two CTEs to include just the pictures needed
(
SELECT *
FROM B
FOR XML PATH('img')
) +
'</images>
</root>
' AS XML) AS CastedToXml
FROM A
)
--We use a simple "for .. in .." loop to travers down the nodes and return their content "as-is" or replaced by the corresponding image
SELECT CastedToXml.query('for $nd in /root/row/*
return
if(local-name($nd)="img") then
/root/images/img[id=$nd/@index]/p/text()
else
$nd/text()
').value('.','nvarchar(max)')
FROM Casted
GO
-清理:认真处理真实数据!
DROP TABLE B;
GO
DROP TABLE A;
一个转换后的XML看起来像这样:
<root>
<row>
<text>Another example with a picutre </text>
<img index="2" />
<text> here and one more here </text>
<img index="3" />
<text />
</row>
<images>
<img>
<id>1</id>
<p>THIS IS PICTURE 1</p>
</img>
<img>
<id>2</id>
<p>THIS IS PICTURE 2</p>
</img>
<img>
<id>3</id>
<p>THIS IS PICTURE 3</p>
</img>
</images>
</root>
结果是
A picture is right here THIS IS PICTURE 1 and then here is some text and another picture THIS IS PICTURE 2 and here is the end
Another picture is here THIS IS PICTURE 1 and yet more text and another picture THIS IS PICTURE 2 and here is the end
Another example with a picutre THIS IS PICTURE 2 here and one more here THIS IS PICTURE 3