我有以下简单的表格
Item (id, name, date, fixed_position)
(1, 'first entry', '2016-03-09 09:00:00', NULL)
(2, 'second entry', '2016-03-09 04:00:00', 1)
(3, 'third entry', '2016-03-09 05:00:00', NULL)
(4, 'fourth entry', '2016-03-09 19:00:00', NULL)
(5, 'fifth entry', '2016-03-09 13:00:00', 4)
(6, 'sixth entry', '2016-03-09 21:00:00', 2)
项目数量不固定,实际上可以在~100到~1000之间变化。
我想要实现的是执行查询以返回由date
字段排序的项目集合,该字段考虑fixed_position
字段,其代表类似“固定”结果到特定位置的内容。如果给定条目的fixed_position
不为NULL,则结果应固定到第n个位置,如果fixed_position
为NULL,则ORDER BY
应优先。
查询的理想输出以获得更明确的解释:
(2, 'second entry', '2016-03-09 04:00:00', 1) // pinned to 1-st position
(6, 'sixth entry', '2016-03-09 21:00:00', 2) // pinned to 2-nd position
(3, 'third entry', '2016-03-09 05:00:00', NULL) // ORDER BY `date`
(5, 'fifth entry', '2016-03-09 13:00:00', 4) // pinned to 4-th position
(1, 'first entry', '2016-03-09 09:00:00', NULL) // ORDER BY `date`
(4, 'fourth entry', '2016-03-09 19:00:00', NULL) // ORDER BY `date`
我已尝试在Ordering MySql results when having fixed position for some items中发布解决方案,但即使使用复制粘贴方法,这似乎根本不起作用。
我迄今为止尝试的是这个查询:
SELECT
@i := @i +1 AS iterator,
t.*,
COALESCE(t.fixed_position, @i) AS positionCalculated
FROM
Item AS t,
(
SELECT
@i := 0
) AS foo
GROUP BY
`id`
ORDER BY
positionCalculated,
`date` DESC
返回:
iterator | id | name | date | fixed_position | positionCalculated
1 1 first entry 2016-03-09 09:00:00 NULL 1
2 2 second entry 2016-03-09 04:00:00 1 1
6 6 sixth entry 2016-03-09 21:00:00 2 2
3 3 third entry 2016-03-09 05:00:00 NULL 3
4 4 fourth entry 2016-03-09 19:00:00 NULL 4
5 5 fifth entry 2016-03-09 13:00:00 4 4
MySQL是否可以执行此类任务,还是应该采用后端方法并在两个结果集上执行PHP array_merge()
?
答案 0 :(得分:3)
解决这个问题的强力方法是首先创建一个比原始表大的行数的计数表:
SELECT @rn := @rn + 1 AS rn
FROM (
SELECT 1 AS x UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1) AS t1
CROSS JOIN (
SELECT 1 AS x UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1) AS t2
CROSS JOIN (SELECT @rn := 0) AS v
然后,您可以将此表连接到包含原始表的所有固定位置的派生表:
SELECT Tally.rn
FROM (
... tally table query here
) AS Tally
LEFT JOIN (
SELECT fixed_position
FROM Item
) AS t ON Tally.rn = t.fixed_position
WHERE t.t.fixed_position IS NULL
以上内容将返回待填补的缺失订单位置。
您现在可以使用上面的查询作为另一个连接到原始表的派生表来实现所需的排序:
SELECT id, name, `date`, fixed_position, Gaps.rn,
derived.seq, Gaps.seq
FROM (
SELECT id, name, `date`, fixed_position,
@seq1 := IF(fixed_position IS NULL, @seq1 + 1, @seq1) AS seq
FROM Item
CROSS JOIN (SELECT @seq1 := 0) AS v
ORDER BY `date`
) AS derived
LEFT JOIN (
SELECT Tally.rn,
@seq2 := @seq2 + 1 AS seq
FROM (
SELECT @rn := @rn + 1 AS rn
FROM (
SELECT 1 AS x UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1) AS t1
CROSS JOIN (
SELECT 1 AS x UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1) AS t2
CROSS JOIN (SELECT @rn := 0) AS v
) AS Tally
LEFT JOIN (
SELECT fixed_position
FROM Item
) AS t ON Tally.rn = t.fixed_position
CROSS JOIN (SELECT @seq2 := 0) AS v
WHERE t.t.fixed_position IS NULL
ORDER BY rn
) AS Gaps ON (derived.seq = Gaps.seq) AND (derived.fixed_position IS NULL)
ORDER BY COALESCE(derived.fixed_position, Gaps.rn)
答案 1 :(得分:0)
如果有人偶然发现这一点,我在 dk1 的回答的帮助下想出了一个很好的解决方案。虽然他的回答原则上是好的,但它有一个缺陷,即固定行的每次迭代都将其位置增加一。例如:固定在第 1 个位置的帖子位于第 1 个位置 (+0),固定到第 2 个位置的帖子位于第 3 个位置 (+1),固定到第 4 个位置的帖子位于第 6 个位置 (+2 ).
我通过添加第二个变量来减少固定帖子的最终位置来解决这个问题。
代码说明:
minor
= 带有未固定帖子的派生表major
= 带有固定帖子的派生表@rownum
= 次要表中帖子的迭代器@decrease
= 主要表中帖子的迭代器SELECT
final.*
FROM
(
(
# see note #1 about this wrapper query
SELECT * FROM
(
SELECT
minor.*,
(@rownum := @rownum + 1) AS 'rowPosition',
0 AS 'rowDecrease',
0 AS 'majorEntry'
FROM table_with_your_posts minor
JOIN (@rownum := 0) x
WHERE minor.your_fixed_position IS NULL
ORDER BY minor.your_date DESC
) y
ORDER BY y.your_date DESC
)
UNION ALL
(
SELECT
major.*,
major.your_fixed_position AS 'rowPosition',
(@decrease := @decrease + 1) AS 'rowDecrease',
1 AS 'majorEntry'
FROM table_with_your_posts major
JOIN (@decrease := -1) x
WHERE major.your_fixed_position IS NOT NULL
ORDER BY major.your_fixed_position ASC
)
) final
# see note #2 about this ORDER BY clause
ORDER BY final.rowPosition - final.rowDecrease ASC, final.majorEntry DESC
注意#1:现在我不是专家,但是如果您不将次表的查询包装在另一个查询中并且不按照与内部查询中相同的内容对其进行排序,则排序次要表不起作用。也许有人可以对此有所了解。
注意 #2:ORDER BY final.rowPosition - final.rowDecrease - final.majorEntry / 2 ASC
也有效且看起来更酷
请注意,如果您想进一步过滤结果(这里的好例子是仅显示已发布的帖子),您很可能需要将过滤条件添加到两个派生表的 WHERE 子句中。否则最终位置将不正确。
最后我想补充一点,这不是万无一失的。例如,如果有两个位置相同的固定帖子,它会将结果移动一个。这可以通过使列具有固定位置 UNIQUE 来解决。我想到的另一件事是,如果表格的结果少于固定帖子的位置值,则该帖子显然不会位于其所需的位置(例如,如果表格包含 5 个帖子并且您将帖子固定到第 8 个帖子)位置,它可能最终会排在第 5 位)。请记住,我没有对此进行有意义的测试,请确保您的查询有效满足您的需求。