【问题标题】:Is there any way to dynamically define an operator based on a CASE in MySQL?有没有办法根据 MySQL 中的 CASE 动态定义运算符?
【发布时间】:2016-12-02 13:42:25
【问题描述】:

我有一个查询,它自行连接以获取每个唯一列表的最新或最旧订单记录。

SELECT
    lists.ordered,
    cnt1.*
FROM 
    cnt_lists as cnt1
    LEFT JOIN 
        lists
        on 
            cnt1.list_id = lists.id
    LEFT JOIN 
        cnt_lists as cnt2
        on 
            (cnt1.list_id = cnt2.list_id AND cnt1.id < cnt2.id)

WHERE
    cnt2.id is null and cnt1.list_id in ('3176', '3295', '3296') and cnt1.listable_type = 'Movie';

这个查询很好用,但是lists.ordered 可以是 0 或 1。当lists.ordered = 0 我希望on 语句中的运算符为cnt1.id &lt; cnt2.id 但当lists.ordered = 1 我想要它被反转cnt1.id &gt; cnt2.id

有没有办法根据CASE 语句动态定义运算符?下面的方法不起作用,但我在玩想法。

SELECT
    lists.ordered,
    CASE
        WHEN lists.ordered = 1 THEN '>'
        ELSE '<'
    END AS operator,
    cnt1.*
FROM 
    cnt_lists as cnt1
    LEFT JOIN 
        lists
        on 
            cnt1.list_id = lists.id
    LEFT JOIN 
        cnt_lists as cnt2
        on 
            (cnt1.list_id = cnt2.list_id AND cnt1.id operator cnt2.id)

WHERE
    cnt2.id is null and cnt1.list_id in ('3176', '3295', '3296') and cnt1.listable_type = 'App\\Models\\Movie';

如何为每个列表同时提取最高顺序和最低顺序,我可以确定在 PHP 端使用这两个记录中的哪一个?

我只是在寻找想法,因为我试图避免必须单独查询每个列表的 N + 1 查询问题。

【问题讨论】:

    标签: php mysql join left-join


    【解决方案1】:

    依赖动态查询很麻烦。您最好将该运算符拉入查询的 WHERE 部分并将其与 OR 配对:

    SELECT
        lists.ordered,
        cnt1.*
    FROM 
        cnt_lists as cnt1
        LEFT JOIN 
            lists
            on 
                cnt1.list_id = lists.id
        LEFT JOIN 
            cnt_lists as cnt2
            on 
                (cnt1.list_id = cnt2.list_id)
    
    WHERE
        cnt2.id is null and cnt1.list_id in ('3176', '3295', '3296') 
        and cnt1.listable_type = 'Movie'
        AND (
           cnt2.id IS NULL /* Including this here because you have a LEFT JOIN */
           (lists.ordered = 1 AND cnt1.id < cnt2.id) OR 
           (lists.ordered = 0 AND cnt1.id > cnt2.id)
        );
    

    您的另一个选择是直接将该逻辑放入连接逻辑中。这可能更容易阅读。

    SELECT
        lists.ordered,
        cnt1.*
    FROM 
        cnt_lists as cnt1
        LEFT JOIN 
            lists
            on 
                cnt1.list_id = lists.id
        LEFT JOIN 
            cnt_lists as cnt2
            on 
                (cnt1.list_id = cnt2.list_id AND (
           (lists.ordered = 1 AND cnt1.id < cnt2.id) OR 
           (lists.ordered = 0 AND cnt1.id > cnt2.id)
        ))
    
    WHERE
        cnt2.id is null and cnt1.list_id in ('3176', '3295', '3296') 
        and cnt1.listable_type = 'Movie'
        ;
    

    您可以选择在最终输出中包含 &lt;,但这不会特别有用。

    如果你*REALLY*想要创建一个动态查询,很难一步完成,but it is definitely doable

    【讨论】:

    • 我正在使用 join 语句中的逻辑探索您的第二个选项,但我看到了一件奇怪的事情。在这种情况下,(lists.ordered = 1 AND cnt1.id &lt; cnt2.id) 它使用order = 2 而不是order = 2 拉动cnt_lists 行,即使知道有order = 1 记录。另一个用例是正确提取最后一个订单。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-21
    • 1970-01-01
    • 2019-12-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多