带有 ORDER BY CASE 子句的范围无法正常工作
Scope with ORDER BY CASE clause not working properly
我的 rails 模型上有一个范围,可以帮助我对对象进行分类。如下所示:
scope :active, ->(u = nil, now = "NOW()") {
published_and_private(u).eager_load(:images)
.where("(listing = 1 AND complete = 0) OR " + # LISTING
"(online_only = 1 AND scheduled_end_time + INTERVAL 1 DAY >= #{now}) OR " + # TIMED
"(online_only = 0 AND listing = 0 AND starts_at + INTERVAL 1 DAY >= #{now})") # LIVE
.order("complete, CASE WHEN sort_index IS NOT NULL THEN sort_index " +
"WHEN scheduled_end_time IS NOT NULL THEN scheduled_end_time " +
"WHEN starts_at IS NOT NULL THEN starts_at ELSE #{now} + INTERVAL 10 YEAR END")
}
下面是查询得到运行时返回的数据库中的数据:
select id, name, complete, sort_index, starts_at, scheduled_end_time from auctions where published = 1 ORDER BY complete, CASE WHEN sort_index IS not NULL THEN sort_index WHEN scheduled_end_time IS NOT NULL THEN scheduled_end_time WHEN starts_at IS NOT NULL THEN starts_at ELSE (NOW() + INTERVAL 10 YEAR) END;
+----+-----------------------------------+----------+------------+---------------------+---------------------+
| id | name | complete | sort_index | starts_at | scheduled_end_time |
+----+-----------------------------------+----------+------------+---------------------+---------------------+
| 21 | Listing: Mountain Cabin Estate | 0 | 1 | NULL | NULL |
| 17 | Multi-Item Online Only | 0 | 2 | 2017-08-07 06:48:00 | 2017-08-21 12:48:00 |
| 9 | Multi-item Live Auction | 0 | NULL | 2017-08-21 18:48:02 | NULL |
| 19 | Many Item LIVE Auction | 0 | NULL | 2017-08-21 18:48:02 | NULL |
| 10 | Single Item Online Only | 0 | NULL | 2017-08-07 18:48:03 | 2017-08-22 00:48:02 |
| 18 | MANY Item Timed Auction | 0 | NULL | 2017-08-07 18:48:03 | 2017-08-22 00:48:02 |
| 22 | LISTING: Multi-parcel Real Estate | 0 | NULL | NULL | NULL |
| 20 | Bad Images | 0 | 3 | 2017-08-21 14:48:00 | NULL |
| 8 | Single Item Live Auction | 1 | NULL | 2017-08-21 18:48:02 | NULL |
+----+-----------------------------------+----------+------------+---------------------+---------------------+
我的问题是排序索引为 3 的对象不合适,任何超过 2 的数字都会发生这种情况,我完全不知道为什么会这样。我期望查询将该对象放在 sort_index 为 2.
的那个对象的正下方
如有任何帮助、指导或见解,我们将不胜感激。
你能试试用 ISNULL
代替吗,例如:
.order("complete, ISNULL(sort_index), sort_index, " +
"ISNULL(scheduled_end_time), scheduled_end_time " +
"ISNULL(starts_at), starts_at")
无法将所有这些条件放入 CASE... WHEN
。 CASE ... WHEN
在 WHERE
子句中有效地创建了一个伪列。所以你所做的没有什么不同:
SELECT *, CASE /* your logic */ END AS sort_logic
/*
Notice that the sort_logic column doesn't actually exist in the table. Instead
MySQL calculates the value and for the duration of this query
*/
WHERE /* <stuff> */
ORDER BY sort_logic
您真正想要的是一系列列值。 @Darshan 上面有一种方法,基本上是创建一系列布尔列并将它们添加到排序中:
complete, ISNULL(sort_index) DESC,
/*
Desc and ASC are important here. You want NOT NULL values to float to
the top, but you want the columns to work in ascending order.
*/
sort_index ASC,
ISNULL(scheduled_end_time) DESC, scheduled_end_time ASC,
ISNULL(starts_at) DESC, starts_at ASC,
您还可以选择将列值默认为 MySQL 最大值。在这种情况下 '9999-12-31'
表示 highest possible date, and ~0
represents the max int value:
complete,
ISNULL(sort_index, ~0),
ISNULL(scheduled_end_time, '9999-12-31'),
ISNULL(starts_at, '9999-12-31')
在这种情况下,逻辑是 "Sort by complete
, if sort_index
isn't null use that for sorting, otherwise throw the result to the end of the list where sort_index
isn't null…",所有其他列也遵循相同的逻辑。
您要查找的订单子句可能是:
order by complete,
Coalesce(
sort_index,
scheduled_end_time,
starts_at,
#{now} + INTERVAL 10 YEAR
)
但是,您正在将整数与此类日期进行比较,所以我不确定它是如何工作的——可能是通过隐式类型转换,这不太可能导致预期的结果。
也许你的意思是:
order by complete,
sort_index,
Coalesce(
scheduled_end_time,
starts_at,
#{now} + INTERVAL 10 YEAR
)
最后尝试使用 -value
技巧对 NULL 进行排序。我也像其他人一样添加了合并,这确实清理了该 case 语句。
ORDER BY
complete,
-sort_index DESC, -- Sort ascending with nulls last
COALESCE(scheduled_end_time, starts_at, now() + INTERVAL 10 YEAR)
;
你也可以查看我的sqlfiddle
我的 rails 模型上有一个范围,可以帮助我对对象进行分类。如下所示:
scope :active, ->(u = nil, now = "NOW()") {
published_and_private(u).eager_load(:images)
.where("(listing = 1 AND complete = 0) OR " + # LISTING
"(online_only = 1 AND scheduled_end_time + INTERVAL 1 DAY >= #{now}) OR " + # TIMED
"(online_only = 0 AND listing = 0 AND starts_at + INTERVAL 1 DAY >= #{now})") # LIVE
.order("complete, CASE WHEN sort_index IS NOT NULL THEN sort_index " +
"WHEN scheduled_end_time IS NOT NULL THEN scheduled_end_time " +
"WHEN starts_at IS NOT NULL THEN starts_at ELSE #{now} + INTERVAL 10 YEAR END")
}
下面是查询得到运行时返回的数据库中的数据:
select id, name, complete, sort_index, starts_at, scheduled_end_time from auctions where published = 1 ORDER BY complete, CASE WHEN sort_index IS not NULL THEN sort_index WHEN scheduled_end_time IS NOT NULL THEN scheduled_end_time WHEN starts_at IS NOT NULL THEN starts_at ELSE (NOW() + INTERVAL 10 YEAR) END;
+----+-----------------------------------+----------+------------+---------------------+---------------------+
| id | name | complete | sort_index | starts_at | scheduled_end_time |
+----+-----------------------------------+----------+------------+---------------------+---------------------+
| 21 | Listing: Mountain Cabin Estate | 0 | 1 | NULL | NULL |
| 17 | Multi-Item Online Only | 0 | 2 | 2017-08-07 06:48:00 | 2017-08-21 12:48:00 |
| 9 | Multi-item Live Auction | 0 | NULL | 2017-08-21 18:48:02 | NULL |
| 19 | Many Item LIVE Auction | 0 | NULL | 2017-08-21 18:48:02 | NULL |
| 10 | Single Item Online Only | 0 | NULL | 2017-08-07 18:48:03 | 2017-08-22 00:48:02 |
| 18 | MANY Item Timed Auction | 0 | NULL | 2017-08-07 18:48:03 | 2017-08-22 00:48:02 |
| 22 | LISTING: Multi-parcel Real Estate | 0 | NULL | NULL | NULL |
| 20 | Bad Images | 0 | 3 | 2017-08-21 14:48:00 | NULL |
| 8 | Single Item Live Auction | 1 | NULL | 2017-08-21 18:48:02 | NULL |
+----+-----------------------------------+----------+------------+---------------------+---------------------+
我的问题是排序索引为 3 的对象不合适,任何超过 2 的数字都会发生这种情况,我完全不知道为什么会这样。我期望查询将该对象放在 sort_index 为 2.
的那个对象的正下方如有任何帮助、指导或见解,我们将不胜感激。
你能试试用 ISNULL
代替吗,例如:
.order("complete, ISNULL(sort_index), sort_index, " +
"ISNULL(scheduled_end_time), scheduled_end_time " +
"ISNULL(starts_at), starts_at")
无法将所有这些条件放入 CASE... WHEN
。 CASE ... WHEN
在 WHERE
子句中有效地创建了一个伪列。所以你所做的没有什么不同:
SELECT *, CASE /* your logic */ END AS sort_logic
/*
Notice that the sort_logic column doesn't actually exist in the table. Instead
MySQL calculates the value and for the duration of this query
*/
WHERE /* <stuff> */
ORDER BY sort_logic
您真正想要的是一系列列值。 @Darshan 上面有一种方法,基本上是创建一系列布尔列并将它们添加到排序中:
complete, ISNULL(sort_index) DESC,
/*
Desc and ASC are important here. You want NOT NULL values to float to
the top, but you want the columns to work in ascending order.
*/
sort_index ASC,
ISNULL(scheduled_end_time) DESC, scheduled_end_time ASC,
ISNULL(starts_at) DESC, starts_at ASC,
您还可以选择将列值默认为 MySQL 最大值。在这种情况下 '9999-12-31'
表示 highest possible date, and ~0
represents the max int value:
complete,
ISNULL(sort_index, ~0),
ISNULL(scheduled_end_time, '9999-12-31'),
ISNULL(starts_at, '9999-12-31')
在这种情况下,逻辑是 "Sort by complete
, if sort_index
isn't null use that for sorting, otherwise throw the result to the end of the list where sort_index
isn't null…",所有其他列也遵循相同的逻辑。
您要查找的订单子句可能是:
order by complete,
Coalesce(
sort_index,
scheduled_end_time,
starts_at,
#{now} + INTERVAL 10 YEAR
)
但是,您正在将整数与此类日期进行比较,所以我不确定它是如何工作的——可能是通过隐式类型转换,这不太可能导致预期的结果。
也许你的意思是:
order by complete,
sort_index,
Coalesce(
scheduled_end_time,
starts_at,
#{now} + INTERVAL 10 YEAR
)
最后尝试使用 -value
技巧对 NULL 进行排序。我也像其他人一样添加了合并,这确实清理了该 case 语句。
ORDER BY
complete,
-sort_index DESC, -- Sort ascending with nulls last
COALESCE(scheduled_end_time, starts_at, now() + INTERVAL 10 YEAR)
;
你也可以查看我的sqlfiddle