【问题标题】:simulate row_number with variables ignore order使用变量忽略顺序模拟 row_number
【发布时间】:2019-08-01 21:56:39
【问题描述】:

我试图帮助解决这个问题Selecting rows based on a few rules

这个想法是为每个用户选择与@lenguage 匹配的语言的行 ID,否则选择创建的第一种语言。

因为没有row_number(),不得不使用用户变量。考虑我添加一个字段test_id,这样可以将所有案例放在同一张桌子上。

SQL DEMO

  SELECT t.* , (`language` = @language) as tt,
         @rn := if (@partition = CONCAT(`test_id`, '-', `user`),
                    @rn + 1,
                    if(@partition := CONCAT(`test_id`, '-', `user`), 1, 1)
                   ) as rn,
         @partition           
  FROM Table1 t
  CROSS JOIN ( SELECT @language := 'de', @rn := 0, @partition := '' ) as var
  ORDER BY CONCAT(`test_id`, '-', `user`), 
           (`language` = @language) DESC,
           `created`

输出

但即使ORDER BY 给出了正确的排序,分区1-43-4 也不会将语言'de' 放在首位。所以有些东西正在改变变量@rn 的分配方式。

| test_id | id | title | language |              created | user | tt | rn | @partition |
|---------|----|-------|----------|----------------------|------|----|----|------------|
|       1 |  3 |     c |       de | 2019-01-03T00:00:00Z |    4 |  1 |  3*|        1-4 |
|       1 |  1 |     a |       en | 2019-01-01T00:00:00Z |    4 |  0 |  1 |        1-4 |
|       1 |  2 |     b |       es | 2019-01-02T00:00:00Z |    4 |  0 |  2 |        1-4 |
|       2 |  1 |     a |       en | 2019-01-01T00:00:00Z |    4 |  0 |  1 |        2-4 |
|       2 |  2 |     b |       es | 2019-01-02T00:00:00Z |    4 |  0 |  2 |        2-4 |
|       3 |  1 |     a |       en | 2019-01-01T00:00:00Z |    3 |  0 |  1 |        3-3 |
|       3 |  3 |     b |       de | 2019-01-03T00:00:00Z |    4 |  1 |  2*|        3-4 |
|       3 |  2 |     b |       es | 2019-01-02T00:00:00Z |    4 |  0 |  1 |        3-4 |
|       3 |  4 |     c |       de | 2019-01-04T00:00:00Z |    5 |  1 |  1 |        3-5 |

【问题讨论】:

  • MySQL 手册说了一些关于 “涉及用户变量的表达式的评估顺序未定义。” 很确定这发生在这里.. @Barmar 的建议接缝是正确的..
  • @Barmar 我为调试添加了 tt,我在 order by 中有 (language = @language) DESC。除非您的意思是我必须先创建一个子查询才能在执行@rn 之前进行排序?
  • 你期望的完整输出是什么,你应该期待de在组中的第一个吗?
  • 是的@RaymondNijland 这就是为什么我使用if() 来确保应用正确的顺序。变量只在if()内改变
  • @RaymondNijland for partition 1-4 我希望“de”是 1,然后是 2,3。顺序正确,但变量应该是1,2,3 而不是3,1,2

标签: mysql sql user-variables


【解决方案1】:

在选择所有行后进行排序。您的 @rn 变量是在行选择期间设置的,因此它使用行的内部顺序,而不是您的 ORDER BY 子句中指定的顺序。

您需要将排序移动到子查询中,然后在主查询中计算@rn

  SELECT t.*,
         @rn := if (@partition = CONCAT(`test_id`, '-', `user`),
                    @rn + 1,
                    if(@partition := CONCAT(`test_id`, '-', `user`), 1, 1)
                   ) as rn,
         @partition           
  FROM (
    SELECT *, (language = @language) AS tt
    FROM Table
    CROSS JOIN (SELECT @language := 'de') AS var
    ORDER BY CONCAT(test_id, '-', user),
            tt DESC,
            created
  ) AS t
  CROSS JOIN ( SELECT @rn := 0, @partition := '' ) as var

【讨论】:

  • 我看到了这个工作,但为什么sqlfiddle.com/#!9/69d52b/10 不需要子查询?
  • 当您直接对表列进行排序时,MySQL 会执行优化并在提取期间进行排序。但是问题中的查询是按计算值排序的,因此无法按该顺序从表中提取。
猜你喜欢
  • 2021-06-03
  • 1970-01-01
  • 2014-03-09
  • 2011-02-10
  • 1970-01-01
  • 2018-08-07
  • 2023-04-10
  • 2016-02-12
  • 1970-01-01
相关资源
最近更新 更多