【问题标题】:T-SQL: LIKE operator, compare string to one valueT-SQL:LIKE 运算符,将字符串与一个值进行比较
【发布时间】:2021-11-28 07:50:07
【问题描述】:

有没有办法将一列中的多个值与另一列中的单个值进行比较。

例子:

  • A 列包含:[a;b;c;d]
  • B 列包含:[a]

目前我正在使用LIKE 运算符来实现这一点,但不是结果。我用通配符% 尝试了它,但由于;,我没有匹配到。

【问题讨论】:

  • 看来您应该修复您的设计,而不是在数据库中存储分隔数据。
  • 这不是我的数据库,它在客户站点。只需要快速修复。
  • “快速”修复。 ;)
  • 字符串拆分是 SO 上最常见的问题 - 稍作搜索就会发现许多示例适合您使用的任何版本的 SQL Server。
  • 请始终使用适当的标签指定您的 SQL Server 版本。

标签: sql-server tsql sql-like


【解决方案1】:

正如 Larnu 所建议的,这里真正的解决方法是修复设计。您应该回到所有者那里并提醒他们数据库是用于存储关系数据的;如果您将多个“事实”塞入单个列中,那么您不妨使用平面文件。例外情况是,如果您为应用程序存储一个逗号分隔的列表,并且只有应用程序负责组装和分解该集合。

无论如何,鉴于您可能对此感到困惑(假设ColumnA 限制为 128 个字符):

CREATE TABLE dbo.BadDesign
(
  ColumnA nvarchar(128),
  ColumnB nvarchar(max)
);
 
INSERT dbo.BadDesign(ColumnA, ColumnB) VALUES
  (N'[a]', N'[a;b;c;d]'), -- only match
  (N'[p]', N'[q;r;s]'),
  (N'[h]', N'[hi;j;k]');

您可以在this db<>fiddle看到依次演示的以下解决方案:


嵌套替换

在过去(SQL Server 2017)之前,我们会执行嵌套的REPLACE() 调用来去掉方括号并用分隔符替换字符串的每一端:

-- All versions
 
SELECT ColumnA, ColumnB
FROM dbo.BadDesign 
WHERE REPLACE(REPLACE(ColumnB, N'[',N';'),N']', N';')
      LIKE N'%' + REPLACE(REPLACE(ColumnA ,N'[',N';'),N']', N';') + N'%';

毛,但结果:

ColumnA ColumnB
[a] [a;b;c;d]

我们不能在 SQL Server 2017 之前的版本上使用 TRIM(),但我在下面解释了为什么我们不想在现代版本上使用该功能。


OpenJson

在 2016+ 中,我们可以在对字符串进行一些操作后使用OPENJSON。在这里,我使用了 PARSENAME() 技巧,只有在 ColumnA this db<>fiddle 中展示了其他解决方法:

SELECT b.ColumnA, b.ColumnB 
  FROM dbo.BadDesign AS b
  CROSS APPLY OPENJSON(REPLACE(REPLACE(REPLACE(
    b.ColumnB, N'[', N'["'), N']', N'"]"'), N';', N'","')) AS j
  WHERE j.value = PARSENAME(b.ColumnA, 1);

结果:

ColumnA ColumnB
[a] [a;b;c;d]

翻译

在 SQL Server 2017 中,使用TRANSLATE() 可以稍微少一些:

-- SQL Server 2017+
 
SELECT ColumnA, ColumnB 
FROM dbo.BadDesign
WHERE TRANSLATE(ColumnB, N'[]',N';;')
      LIKE N'%' + TRANSLATE(ColumnA, N'[]',N';;') + N'%';  
ColumnA ColumnB
[a] [a;b;c;d]

我们不想在这里使用TRIM(),因为我们不只是想删除封闭的方括号;我们需要分隔符,因此无论 B 在字符串中的哪个位置,我们都可以始终将 A 与 B 进行比较。无需替换或翻译周围的分隔符,we could get inaccurate results if the match is at the beginning or end of the multi-value string


分割函数

或者,您可以在 SQL Server 2016+ 上创建此函数(或者在早期版本中不使用 STRING_SPLIT() 的更混乱的函数 - 正如 Smor 指出的那样,搜索会出现数百个):

CREATE FUNCTION dbo.SplitAndClean(@s nvarchar(max))
RETURNS TABLE
WITH SCHEMABINDING
AS
  RETURN 
  (
    SELECT value 
      FROM STRING_SPLIT
           (
             -- if 2016:
             REPLACE(REPLACE(@s, N'[',N';'),N']', N';'), 
             -- if 2017+, TRANSLATE() is slightly cleaner:
             /* TRANSLATE(@s, N'[]',N';;'), */
             N';'
           )
      WHERE value > N''
  );

那么你可以说:

SELECT bd.ColumnA, bd.ColumnB
  FROM dbo.BadDesign AS bd
  CROSS APPLY dbo.SplitAndClean(bd.ColumnA) AS a
  CROSS APPLY dbo.SplitAndClean(bd.ColumnB) AS b
  WHERE a.value = b.value;
ColumnA ColumnB
[a] [a;b;c;d]

但最后……

...这些都是掩盖不良设计的粗俗“解决方案”,您真的应该让他们重新考虑他们如何使用数据库。

我知道许多商店不能只切换到passing sets between the app and the database using TVPs,因为一些客户提供商和 ORM 还没有十多年的时间赶上这趟火车。如果不能使用 TVP 或者无法更改 app,至少应该考虑截取 app 传递过来的逗号分隔列表,并使用SPLIT_STRING() 之类的方式将其拆分。然后,您可以以关系方式存储值,让数据库执行数据库的设计任务,而不受应用程序限制的束缚。

【讨论】:

  • Erm TRIM('[]' FROM @s) 也许,而不是 REPLACE(REPLACE(?
  • @Charlieface 已更新以解决您的“Erm”问题——同样,我们不能在旧版本上使用TRIM() 代替嵌套的REPLACE(),因为TRIM() 不存在。在 2017 年以上,TRANSLATE() 在这里更有意义。
【解决方案2】:

如果 col_b 中总是只有一个值,就像您的示例一样,您可以使用嵌套替换函数来删除 [ 和 ],然后使用“like”进行搜索

select *
  from test_data
 where col_a like '%' + replace(replace(col_b, '[', ''), ']', '') + '%';

但是

如果 col_b 中的值可能超过值,并且它可以按任何顺序排列(例如“[a;c]”或“[d;a]”),您将在已回答的问题中找到答案,或者您可以 google对于 msdn 上的“string_split()”函数。后者有很好的示例部分,肯定会帮助你

【讨论】:

  • 我认为您已经交换了列,但您还需要考虑分隔符。否则[ab;c] 将匹配LIKE '%a%'
  • @AaronBertrand 你是对的,我没有想到那种情况。谢谢!
猜你喜欢
  • 2018-05-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多