【问题标题】:How to select multiple key-value pairs into a single column?如何将多个键值对选择到单个列中?
【发布时间】:2021-06-16 05:01:36
【问题描述】:

我有一个数据库,其中包含一个产品表和一个键值对表,用于将随机数据与每个产品相关联。

  • 产品表:productId(pk),名称
  • ProductDetails 表:productId(fk)、名称、值

我想运行类似这样的查询:

SELECT 
  *, 
  (???) as details 
from Products p 
JOIN ProductDetails pd on pd.productId = p.productId
WHERE (??? there exists a value with pd.key = 'foo' and pd.value like '%bar%` ???) 

我想取回如下所示的数据:

productID   name        details
----------- ----------- -----------------------------------------------------------
22          flizbot     {"sponsor": "fred", "foo", "casbar", "planet": "venus"}
354         munchbat    {"foo": "barrel", "power": "over9000"}
...

我该如何编写这样的查询?

【问题讨论】:

  • 在提出问题时,您需要提供一个可重现的最小示例:(1) DDL 和样本数据填充,即 CREATE 表加上 INSERT、T-SQL 语句。 (2) 你需要做什么,即逻辑,以及你在 T-SQL 中的尝试实现。 (3) 基于上述#1 中的样本数据的期望输出。 (4) 你的 SQL Server 版本 (SELECT @@version;)
  • 这能回答你的问题吗? Convert select query results into Json
  • @devlincarnate 不相关,因为 JSON 键需要是动态的,而 SQL 无法做到这一点
  • @Charlieface :我很好奇您是如何知道 json 键需要根据 OP 发布的内容动态变化的。
  • @devlincarnate {"foo": "barrel"pd.key = 'foo' and pd.value like '%bar%`` seems quite clear to me that key foo` 是动态的,因为它来自 key

标签: sql sql-server key-value


【解决方案1】:

这里实际上有两个独立的问题

  1. 如何根据组中的值进行过滤

    这只是简单地用HAVING 子句和条件COUNT回答
    HAVING COUNT(CASE WHEN pd.[key] = 'foo' AND pd.[value] LIKE '%bar%' THEN 1 END) > 0;

  2. 如何从列值中获取动态 JSON 键值对

    这更复杂。

    由于FOR JSON 需要将键名作为列,因此如果没有动态 SQL,则无法使用它。

DECLARE @cols = (
    SELECT
)

DECLARE @sql nvarchar(max) = N'
SELECT 
    p.id,
    p.name, 
    pd.details 
FROM Products p
CROSS APPLY (
    SELECT details = (SELECT
' +
(
    SELECT STRING_AGG(CAST(
        QUOTENAME([key]) + N' = MIN(CASE WHEN [key] = ' + QUOTENAME([value], '''') + N' THEN [value] END)'
      AS nvarchar(max)), N',
'   )
    FROM (
        SELECT DISTINCT [key]
        FROM ProductDetails
    ) pd
)
+ N'
    FROM ProductDetails pd
    WHERE pd.productId = p.productId
    HAVING COUNT(CASE WHEN pd.[key] = ''foo'' AND pd.[value] LIKE ''%bar%'' THEN 1 END) > 0
    FOR JSON AUTO, WITHOUT_ARRAY_WRAPPER)
) pd;
';

PRINT @sql;  -- for testing

EXEC sp_executesql @sql;

另一种方法是使用 STRING_AGGSTRING_ESCAPE 手动构建 JSON。 我强烈赞成这种解决方案,因为它更易于编写和维护。

SELECT 
    p.id,
    p.name, 
    N'{' +
     STRING_AGG(
        N'"' + STRING_ESCAPE(pd.[key], 'json')
        + N'":"' + STRING_ESCAPE(pd.[value], 'json') + N'"'
      , N','
     ) + N'}'
    AS details 
FROM Products p 
JOIN ProductDetails pd on pd.productId = p.productId
GROUP BY p.id, p.name
HAVING COUNT(CASE WHEN pd.[key] = 'foo' AND pd.[value] LIKE '%bar%' THEN 1 END) > 0;

【讨论】:

  • 这正是我想要的。谢谢!
猜你喜欢
  • 2017-05-20
  • 1970-01-01
  • 2022-06-23
  • 1970-01-01
  • 2023-04-10
  • 2017-10-03
  • 2011-08-30
  • 2018-05-15
  • 1970-01-01
相关资源
最近更新 更多