Как определить строки, сумма которых равна определенной сумме

Я пытаюсь идентифицировать строки в определенном наборе данных с помощью столбца суммы в долларах, сумма которых равна определенной сумме. Я знаю сумму и количество строк, которые в сумме составляют эту сумму.

Параметры: 4 записи на сумму до $15,68

набор данных > 450 тыс. записей

Итак, есть ли способ найти 4 строки, сумма которых равна 15,68 доллара США?

скажем, таблица имеет уникальный идентификатор для каждой строки с именем ID, а затем соответствующий столбец суммы.

ID     Amount
22     $11.55
33     $5.55
44     $3.33
55     $2.33
66     $1.11
77     $.99
88     $-1.77
91     $0.00
99     $-5.00
100    $3.33
111    $4.44
122    $5.00
133    $4.00
144    $2.24

поэтому я хочу получить набор результатов из последних 4 записей из этого набора выборок.

Уточните: 1. Должен ли результат быть четырьмя последовательными строками (упорядоченными по идентификатору), как в вашем примере? 2. Разрешаете ли вы строке появляться в наборе результатов более одного раза?

Thorsten Kettner 27.06.2024 08:56
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
0
1
55
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Один из методов состоит в том, чтобы объединить ваши данные в декартовом виде четыре раза, чтобы создать каждую возможную комбинацию из четырех строк, затем сложить суммы, найти комбинации, которые составляют вашу целевую сумму, а затем снова вернуться к исходным строкам:

WITH data AS (select 22 id,11.55 amount from dual union all
select 33,5.55 from dual union all
select 44,3.33 from dual union all
select 55,2.33 from dual union all
select 66,1.11 from dual union all
select 77,.99 from dual union all
select 88,-1.77 from dual union all
select 91,0.00 from dual union all
select 99,-5.00 from dual union all
select 100,3.33 from dual union all
select 111,4.44 from dual union all
select 122,5.00 from dual union all
select 133,4.00 from dual union all
select 144,2.24 from dual)
SELECT DISTINCT data.*
  FROM (SELECT d1.id id1,
               d2.id id2,
               d3.id id3,
               d4.id id4,
               d1.amount + d2.amount + d3.amount + d4.amount total_amount
          FROM data d1,
               data d2,
               data d3,
               data d4) x,
       data
 WHERE x.total_amount = 15.68
   AND data.id IN (x.id1,x.id2,x.id3,x.id4)

Вероятно, вы также можете использовать иерархический запрос (CONNECT BY...) и оконную функцию для агрегирования по пути иерархии. Это было бы более гибко, поскольку не было бы структурно жестко запрограммировано ровно 4 значения.

Это объединит строки друг с другом, поэтому, если у вас есть одна строка с суммой 15.68/4, она будет сопоставлена ​​рабочий пример

MT0 27.06.2024 00:05

@MT0: Хорошая мысль, но, по моему мнению, в запросе нет ясности по этому поводу. ОП придется решить, хотят ли они разрешить несколько строк в наборе результатов или нет.

Thorsten Kettner 27.06.2024 08:33
Ответ принят как подходящий

Используйте INNER JOIN, чтобы соединить таблицу сама с собой 4 раза, и объединяйте так, чтобы значения располагались в порядке возрастания, чтобы вы не повторяли комбинации в разных порядках или строки, неоднократно соединяемые сами с собой:

SELECT d1.id AS id1,
       d1.amount AS amount1,
       d2.id AS id2,
       d2.amount AS amount2,
       d3.id AS id3,
       d3.amount AS amount3,
       d4.id AS id4,
       d4.amount AS amount4
FROM   data d1
       INNER JOIN data d2
       ON (d1.id < d2.id)
       INNER JOIN data d3
       ON (d2.id < d3.id)
       INNER JOIN data d4
       ON (d3.id < d4.id)
WHERE  d1.amount + d2.amount + d3.amount + d4.amount = 15.68;

Какие выходы:

ID1 СУММА1 ID2 СУММА2 ID3 AMOUNT3 ID4 СУММА4 111 4.44 122 5 133 4 144 2.24

Если вы хотите, чтобы значения были в виде строк, тогда UNPIVOT:

SELECT id,
       amount
FROM   (
  SELECT d1.id AS id1,
         d1.amount AS amount1,
         d2.id AS id2,
         d2.amount AS amount2,
         d3.id AS id3,
         d3.amount AS amount3,
         d4.id AS id4,
         d4.amount AS amount4
  FROM   data d1
         INNER JOIN data d2
         ON (d1.id < d2.id)
         INNER JOIN data d3
         ON (d2.id < d3.id)
         INNER JOIN data d4
         ON (d3.id < d4.id)
  WHERE  d1.amount + d2.amount + d3.amount + d4.amount = 15.68
)
UNPIVOT (
  (amount, id) FOR type IN (
    (amount1, id1) AS 1,
    (amount2, id2) AS 2,
    (amount3, id3) AS 3,
    (amount4, id4) AS 4
  )
);

Какие выходы:

ИДЕНТИФИКАТОР КОЛИЧЕСТВО 111 4.44 122 5 133 4 144 2.24

Вы также можете решить эту проблему с помощью рекурсивного запроса:

WITH hierarchy (id1, amount1, id2, amount2, id3, amount3, id4, amount4, lvl, total) AS (
  SELECT id, amount,
         CAST(NULL AS NUMBER), CAST(NULL AS NUMBER),
         CAST(NULL AS NUMBER), CAST(NULL AS NUMBER),
         CAST(NULL AS NUMBER), CAST(NULL AS NUMBER),
         1, amount
  FROM   data
 UNION ALL
  SELECT h.id1,
         h.amount1,
         CASE lvl + 1 WHEN 2 THEN d.id     ELSE h.id2 END,
         CASE lvl + 1 WHEN 2 THEN d.amount ELSE h.amount2 END,
         CASE lvl + 1 WHEN 3 THEN d.id     ELSE h.id3 END,
         CASE lvl + 1 WHEN 3 THEN d.amount ELSE h.amount3 END,
         CASE lvl + 1 WHEN 4 THEN d.id     ELSE h.id4 END,
         CASE lvl + 1 WHEN 4 THEN d.amount ELSE h.amount4 END,
         lvl + 1, amount + total
  FROM   data d
         INNER JOIN hierarchy h
         ON COALESCE(h.id3, h.id2, h.id1) < d.id AND h.lvl < 4
)
SELECT id, amount
FROM   (
  SELECT *
  FROM   hierarchy
  WHERE  lvl = 4
  AND    total = 15.68
)
UNPIVOT (
  (amount, id) FOR type IN (
    (amount1, id1) AS 1,
    (amount2, id2) AS 2,
    (amount3, id3) AS 3,
    (amount4, id4) AS 4
  )
);

Что также выводит:

ИДЕНТИФИКАТОР КОЛИЧЕСТВО 111 4.44 122 5 133 4 144 2.24

рабочий пример

Нет необходимости использовать дорогостоящие множественные самосоединения, вы можете упростить задачу, используя аналитические функции Sum() Over() и LAG() Over() с предложением оконного режима (... Rows Between ...).
Мощность/стоимость — 1/34.

WITH      --  S a m p l e    D a t a : 
    tbl AS 
      ( Select 22 ID,  11.55 AMOUNT From Dual Union All
        Select 33, 5.55 From Dual Union All
        Select 44, 3.33 From Dual Union All
        Select 55, 2.33 From Dual Union All
        Select 66, 1.11 From Dual Union All
        Select 77, .99 From Dual Union All
        Select 88, -1.77 From Dual Union All
        Select 91, 0.00 From Dual Union All
        Select 99, -5.00 From Dual Union All
        Select 100, 3.33 From Dual Union All
        Select 111, 4.44 From Dual Union All
        Select 122, 5.00 From Dual Union All
        Select 133, 4.00 From Dual Union All
        Select 144, 2.24 From Dual
      ), 

В коде используются параметры, и он должен выдавать результат для любого количества строк (а не только 4) и любой суммы, которую вы привязали к параметрам.
... создайте cte (назовите его Calc), чтобы получить сумму всех строк :NumRowsParameter (4) вместе с начальными и конечными идентификаторами, общий результат которых равен :SumRowsParameter (15.68)

    calc AS
      ( Select  *
        From    ( Select ID, AMOUNT,
                         Sum(AMOUNT) OVER(Order By ID Rows Between :NumRowsParameter - 1 Preceding And Current Row) "SUM_ROWS",
                         LAG(ID, :NumRowsParameter - 1) Over(Order By ID) "ID_START", ID "ID_END"
                  From   tbl
                  Order By ID
                )
        Where   SUM_ROWS = :SumRowsParameter
      )

... внутренне присоедините вашу таблицу к вышеуказанному cte и отфильтруйте результат с помощью предложения ON соединения

--     M a i n    S Q L :
SELECT       tbl.* 
FROM         calc
INNER JOIN   tbl ON(tbl.ID Between calc.ID_START And calc.ID_END)
ORDER BY     tbl.ID
/*    R e s u l t :      ( for :NumRowsParameter = 4 and :SumRowsParameter = 15.68 )
 ID  AMOUNT
---  ------
111    4.44
122       5
133       4
144    2.24  */
/*    R e s u l t :      ( for :NumRowsParameter = 5 and :SumRowsParameter = 13.31 )
 ID  AMOUNT
---  ------
 33    5.55
 44    3.33
 55    2.33
 66    1.11
 77    0.99  */
/*    R e s u l t :      ( for :NumRowsParameter = 1 and :SumRowsParameter = 3.33 )
 ID  AMOUNT
---  ------
 44    3.33
100    3.33    */

Вы думаете, что ОП ищет четыре последовательные строки. Я не. Что ж, желаемый результат в запросе — четыре последовательные строки, поэтому ОП должен уточнить, так ли это просто так или это часть задачи.

Thorsten Kettner 27.06.2024 08:46

@ThorstenKettner Да, я так понял. Я могу ошибаться, конечно. Об этом прямо не говорится, но если они не являются последовательными, то данные выборки и ожидаемый результат ДОЛЖНЫ быть разными.

d r 27.06.2024 08:54

Другие вопросы по теме