【问题标题】:SQL search query with dynamically build where condition动态构建where条件的SQL搜索查询
【发布时间】:2020-06-22 23:06:17
【问题描述】:
Table Orders
| id |
|----|
| 1  |
| 2  |
| 3  |

Table Items
| id |
|----|
| 1  |
| 2  |
| 3  |
| 4  |
| 5  |


Table OrderItems
| order_id | item_id |
|---------------------|
| 1        | 1        |
| 1        | 2        |
| 1        | 3        |
|---------------------|
| 2        | 1        |
| 2        | 4        |
| 2        | 5        |
|---------------------|
| 3        | 1        |
| 3        | 2        |
| 3        | 4        |
| 3        | 5        |

因此用户可以在前端使用运算符ANDOR 和比较EQUAL( = )NOT EQUAL( != ) 构建动态查询。他还可以创建带括号的条件组。

基于这些要求,我将在WHERE 部分中创建动态表达式。 示例:用户想要所有具有项目 1 和项目 2 或 3 而不是项目 4 的订单 表达式构建:

item_id = 1 AND (item_id = 2 OR item_id = 3) AND item_id != 4

根据这个表达式,只有 Order 1 应该被选中。 我的解决方案是 JOIN order_id 上的表 OrderItemsAND 运算符一样经常存在以获得结果:

SELECT o.id 
FROM Orders o
INNER JOIN OrderItems oi1 ON o.id         = oi1.order_id
INNER JOIN OrderItems oi2 ON oi1.order_id = oi2.order_id
INNER JOIN OrderItems oi3 ON oi2.order_id = oi3.order_id
WHERE o1.item_id = 1 AND (oi2.item_id = 2 OR oi2.item_id = 3) AND oi3.item_id != 4
GROUP BY o.id

有没有更优雅的解决方案?如果有更复杂的查询将有越来越多的连接,则可能会出现问题...

【问题讨论】:

    标签: sql postgresql join select where-clause


    【解决方案1】:

    一个选项使用聚合:

    SELECT o.id 
    FROM Orders o
    INNER JOIN OrderItems oi ON o.id = oi.order_id
    GROUP BY o.id
    HAVING
        COUNT(*) FILTER(WHERE oi.item_id = 1) > 0
        AND COUNT(*) FILTER(WHERE oi.item_id IN (2, 3) > 0
        AND COUNT(*) FILTER(WHERE oi.item_id = 4) = 0
    

    以编程方式生成具有更多条件计数的HAVING 子句非常容易。

    您可以通过添加WHERE 子句来对您感兴趣的item_ids 进行预过滤,从而提高查询效率:

    SELECT o.id 
    FROM Orders o
    INNER JOIN OrderItems oi ON o.id = oi.order_id
    WHERE oi.item_id IN (1, 2, 3, 4)
    GROUP BY o.id
    HAVING
        COUNT(*) FILTER(WHERE oi.item_id = 1) > 0
        AND COUNT(*) FILTER(WHERE oi.item_id IN (2, 3) > 0
        AND COUNT(*) FILTER(WHERE oi.item_id = 4) = 0
    

    【讨论】:

    • 我会使用COUNT(DISTINCT oi.item_id) 来处理多次包含相同商品的订单。除此之外,它对我来说看起来不错。
    • @TheImpaler:谢谢。实际上我认为> 0 已经涵盖了这一点。
    【解决方案2】:

    thx @GMB 根据您的聚合方法我想出了这个:

    SELECT oi1.order_id
    FROM (
        SELECT oi.order_id, array_agg(oi.item_id) AS arr_item
        FROM OrderItems oi
        WHERE oi.item_id IN (1, 2, 3, 4)
        GROUP BY oi.order_id
    ) AS oi1
    WHERE 1 = ANY(oi1.arr_item) AND (2 = ANY(oi1.arr_item) OR 3 = ANY(oi1.arr_item)) AND 4 != ALL(oi1.arr_item);
    

    基于EXPLAINHAVING 解决方案中少了一步,因为不需要子查询扫描。我认为从长远来看会更快

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-01-30
      • 1970-01-01
      • 2013-10-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-07-27
      相关资源
      最近更新 更多