【问题标题】:Filter table based on data from json object根据来自 json 对象的数据过滤表
【发布时间】:2018-10-10 05:05:50
【问题描述】:

我正在尝试使用JSONtype 作为存储过程的输入参数,以对返回数据进行一些过滤。

我有以下TableA

CREATE TABLE TableA (
   Id INT NULL,
   Value1 VARCHAR(25) NULL,
   Value2 VARCHAR(25) NULL
);

INSERT INTO TableA (Id, Value1, Value2) values (1, 'test1', 'new1')
INSERT INTO TableA (Id, Value1, Value2) values (1, 'test1', 'new2')
INSERT INTO TableA (Id, Value1, Value2) values (null, null, 'test3')
INSERT INTO TableA (Id, Value1, Value2) values (2, 'myvalue1', 'newvalue')

JSON 参数是动态的 - 表示上表中的一个或多个列名和值。

DECLARE @Filter NVARCHAR(MAX)
SET @Filter=N'{
  "Id": 2, 
  "Value1": "myvalue1",
  "Value2": "newvalue"
}'

然后我从jsoninner join 中提取数据并使用TableA 得到我需要的输出:

...
;WITH cte AS
(
   SELECT * FROM OPENJSON(@Filter)
   WITH(Id INT, 
        Value1 VARCHAR(25),
        Value2 VARCHAR(25))
)
SELECT a.* FROM cte ct
INNER JOIN TableA a
  ON ct.Id = a.Id
INNER JOIN TableA b
  ON ct.Value1 = b.Value1
INNER JOIN TableA c
  ON ct.Value2 = c.Value2

在这个特定的示例中,我得到了所需的输出,因为我指定了所有列。然而,使用JSON 作为参数背后的全部原因是能够动态传递不同的列(实际的表有更多的列)。

如果我想通过以下过滤器,我将不再获得所需的输出,因为inner join

SET @Filter=N'{
  "Id": 2, 
}'
...
SET @Filter=N'{
  "Id": 2, 
  "Value1": "test1"
}'
...
SET @Filter=N'{
  "Value1": "test1, 
  "Value1": "new2"
}'
...etc
  1. 有没有办法从 json 中动态选择所有具有值的对象并跳过空值?或者关于如何解决此问题的任何其他建议?
  2. 有没有办法检查 json 中是否有任何对象?根据文档,只有一个函数 ISJSON 可以验证以确保其格式正确。但是,如果我通过:SET @Filter=N'{}' 它是一个有效的 json 对象,但它是空的。

SQLFIDDLE

【问题讨论】:

  • 顺便说一句:这是一个非常好的问题!可消耗的 DDL 和数据,自己的尝试,清晰的解释 - 我希望我们有更多这样的 :-) +1 从我身边!

标签: sql json sql-server-2017


【解决方案1】:

你可以试试这个:

SELECT a.* 
FROM TableA a
WHERE (a.Id IS NULL AND JSON_VALUE(@Filter,N'$.Id') IS NULL OR a.Id = ISNULL(CAST(JSON_VALUE(@Filter,N'$.Id') AS INT),a.Id))
  AND (a.Value1 IS NULL AND JSON_VALUE(@Filter,N'$.Value1') IS NULL OR a.Value1 = ISNULL(JSON_VALUE(@Filter,N'$.Value1'),a.Value1))
  AND (a.Value2 IS NULL AND JSON_VALUE(@Filter,N'$.Value2') IS NULL OR a.Value2 = ISNULL(JSON_VALUE(@Filter,N'$.Value2'),a.Value2));

诀窍是用实际列的值替换 NULL...

【讨论】:

  • 感谢您的回答。它几乎在呼叫案例中工作,除了一个。如果查看第三条记录,Value2="test3"Id=null and Value=null,这种情况会失败,不会返回记录。我使用的过滤器是SET @Filter=N'{"Value2: "test3"}'
  • 漂亮!感谢您的帮助!
【解决方案2】:

我知道这不是一个真正令人满意的答案,但我在这种情况下使用的方法是将每个可选字段与:

where (value1 is null or field1 = value1)

这消除了使用过滤器显式选择 field1 is null 的能力,并且您需要通过每个参数的两个表达式来炸毁您的 select 语句:

--SELECT * FROM TableA

DECLARE @Filter NVARCHAR(MAX)
SET @Filter=N'{
  "Id": 1, 
  "Value1": "test1",
  "Value2": "new2"
}'
;WITH cte AS
(
  SELECT * FROM OPENJSON(@Filter)
  WITH(Id INT, 
       Value1 VARCHAR(25),
       Value2 VARCHAR(25))
)
SELECT a.* FROM cte filter
INNER JOIN TableA a
  ON     filter.Id = a.Id
     and (filter.Value1 is null or filter.Value1 = a.Value1)
     and (filter.Value2 is null or filter.Value2 = a.Value2)

查询结果

Id  Value1  Value2
1   test1   new2

使用未设置的值进行过滤

如果您的 JSON 中有缺失值,则不会将其用作过滤条件:

--SELECT * FROM TableA

DECLARE @Filter NVARCHAR(MAX)
SET @Filter=N'{
  "Id": 1, 
  "Value2": "new2"
}'
;WITH cte AS
(
  SELECT * FROM OPENJSON(@Filter)
  WITH(Id INT, 
       Value1 VARCHAR(25),
       Value2 VARCHAR(25))
)
SELECT a.* FROM cte filter
INNER JOIN TableA a
  ON     filter.Id = a.Id
     and (filter.Value1 is null or filter.Value1 = a.Value1)
     and (filter.Value2 is null or filter.Value2 = a.Value2)

SQL 输出

Id  Value1  Value2
1   test1   new2

【讨论】:

  • 感谢您的回答。我已经测试了您的代码,并且几乎没有失败的测试用例。在您使用过滤器提供的最后一个示例中,它不应该返回第 1 行,因为我们在 where 子句中有一个 AND。如果我将AND 之后的所有内容都包含在( ...is null OR f.Value1= a.Value1..) 中,它会解决这个问题,但其他测试用例会失败。如果您有任何其他建议,请告诉我。
  • 已更正 - 我缺少括号 - 每个 OR 子句都需要包装,并且我还粘贴了错误的结果。 SQLFiddle 已关闭,因此我目前无法重新检查我的查询,抱歉。
  • 就像我在评论中提到的那样,我已经尝试过了,它解决了您在示例中使用的过滤器的问题。但是,其他测试用例会中断。 SET @Filter=N'{"Value2": "new2"}'SET @Filter=N'{"Value2": "new2", "Value1": "test1"}' 不再返回任何数据。
  • @Corion,问题似乎是,NULL 被视为一个有意义的值,而不仅仅是 missing... 在我的回答中,我使用了一个构造喜欢(a is null and b is null or a=isnull(b,a))
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-07-17
  • 2020-01-10
  • 2020-10-26
  • 1970-01-01
  • 1970-01-01
  • 2022-12-11
  • 1970-01-01
相关资源
最近更新 更多