Объедините два QuerySets или измените порядок одного QS так, чтобы каждый n-й элемент был из второго. - PullRequest
0 голосов
/ 05 ноября 2018

Я не уверен, что это можно сделать, используя только 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.

1 Ответ

0 голосов
/ 05 ноября 2018

Я не знаю, поможет ли это, но этот запрос упорядочивает ваши плоды. Но я считаю, что это можно сделать в цикле (функция SQL или python) более эффективным способом.

демо: дб <> скрипка

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, ...), что является реальной проблемой.


Мой стол :

| 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 |

Первая часть. Получите заказы не яблок:

Использование оконной функции 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: идентификаторы 4,5,6 должны стать 5,6,7; идентификаторы 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, мы просто снова используем функцию row_number(), которая перечисляет строки apple с (1,2,3, ...). Затем мы просто умножаем эти идентификаторы на 4, чтобы получить (4,8,12, ...).

Обе части могут быть объединены UNION и затем должны быть упорядочены по их сгенерированным идентификаторам.

...