合并两个 QuerySets 或重新排序一个 QS,使得每个第 n 个元素都来自第二个

Merge two QuerySets or reorder one QS such that every n-th element will be from the second one

我不确定这是否可以仅使用 Django ORM 或纯 SQL 来完成。我有一个模型 Fruit,我想渲染一个水果列表,使得第 n 个水果都是 type="apple"

所以 4 将是:

我正在寻找一种比一大堆水果更有效的方法,最好是一个 QuerySet 但不确定是否可行。

fruits_except_apples = Fruit.objects.exclude(type='apple')
apples = Fruit.objects.filter(type='apple')

我什至可以创建两个 QuerySets,然后以某种方式合并它们,或者只是重新排序一个 QuerySet

我不知道它是否有帮助,但此查询以您的方式订购水果。但我相信这可以以更有效的方式在循环(SQL 函数或 python)中完成。

demo:db<>fiddle

SELECT 
    name, type
FROM (
    SELECT 
        name, type, 
        row_number + (row_number - 1) / 3 as row_number
    FROM (
        SELECT 
           *,
           row_number() OVER ()
        FROM fruits
        WHERE type != 'apple'
    )s

    UNION

    SELECT
        *,
        4 * row_number() OVER ()
    FROM fruits
    WHERE type = 'apple'
) s
ORDER BY row_number

分步说明:

主要问题是获得具有两种不同行数的订单:一种是步数为 4 (4, 8, 12, ...) 另一种步数为 1 但没有步数为 4 (1,2 ,3,5,6,7,9,...), 这才是真正的问题。


我的table:

| name |      type |
|------|-----------|
|   A1 |     apple |
|   A2 |     apple |
|   A3 |     apple |
|   B1 |    banana |
|   B2 |    banana |
|   B3 |    banana |
|   B4 |    banana |
|   O1 |    orange |
|   K1 |      kiwi |
|   K2 |      kiwi |
|   K3 |      kiwi |
|   C1 | chocolate |
|   C2 | chocolate |

第一部分。获取非苹果订单:

使用 window function row_number。这会在数据中添加一个行计数列:

SELECT 
    *,
    row_number() OVER ()
FROM fruits
WHERE type != 'apple'

name  type       row_number  
----  ---------  ----------  
B1    banana     1           
B2    banana     2           
B3    banana     3           
B4    banana     4           
O1    orange     5           
K1    kiwi       6           
K2    kiwi       7           
K3    kiwi       8           
C1    chocolate  9           
C2    chocolate  10  

现在我们必须创造差距。为此,有必要移动 3 个块:ids 4、5、6 应变为 5、6、7; ids 7,8,9 应该变成 9,10,11 等等。

3的块可以通过在下一个子选择中row_number的整数除法来实现:

SELECT 
     *, 
     row_number / 3
FROM (
    SELECT 
        *,
        row_number() OVER ()
    FROM fruits
    WHERE type != 'apple'
)s

这给出了

name  type       row_number  ?column?  
----  ---------  ----------  --------  
B1    banana     1           0         
B2    banana     2           0         
B3    banana     3           1         
B4    banana     4           1         
O1    orange     5           1         
K1    kiwi       6           2         
K2    kiwi       7           2         
K3    kiwi       8           2         
C1    chocolate  9           3         
C2    chocolate  10          3  

我们可以看到简单的除法还是移位了。因此,减去 1 我们得到了我们期望的结果:

SELECT 
     *, 
     (row_number - 1) / 3
FROM (
    SELECT 
        *,
        row_number() OVER ()
    FROM fruits
    WHERE type != 'apple'
)s   

这给出了

name  type       row_number  ?column?  
----  ---------  ----------  --------  
B1    banana     1           0         
B2    banana     2           0         
B3    banana     3           0         
B4    banana     4           1         
O1    orange     5           1         
K1    kiwi       6           1         
K2    kiwi       7           2         
K3    kiwi       8           2         
C1    chocolate  9           2         
C2    chocolate  10          3   

现在我们可以看到我们可以添加最右边的两列来实现我们的结果:

SELECT 
     *, 
     (row_number - 1) / 3 + row_number
FROM (
    SELECT 
        *,
        row_number() OVER ()
    FROM fruits
    WHERE type != 'apple'
)s  

这给出了

name  type       row_number  ?column?  
----  ---------  ----------  --------  
B1    banana     1           1         
B2    banana     2           2         
B3    banana     3           3         
B4    banana     4           5         
O1    orange     5           6         
K1    kiwi       6           7         
K2    kiwi       7           9         
K3    kiwi       8           10        
C1    chocolate  9           11        
C2    chocolate  10          13   

要获得 apple id,我们只需再次使用 row_number() 函数,它枚举 apple 行(1,2,3,...)。然后我们简单地将这些id乘以4得到(4,8,12,...).

两个部分都可以通过 UNION 连接起来,然后必须按它们生成的 ID 进行排序。