SQL-запрос для всех возможных комбинаций из таблицы - PullRequest
0 голосов
/ 13 мая 2019

У меня есть таблица в результате некоторых вычислений из базы данных SQL, и она выглядит так:

[ID] [PAR1] [PAR2]
[A]   [110]  [0.5]
[B]   [105]  [1.5]
[C]   [120]  [2.0]
[D]   [130]  [3.0]
[E]   [115]  [5.5]
[F]   [130]  [6.5]
[G]   [120]  [7.0]
[H]   [110]  [7.5]
[I]   [105]  [8.0]
[J]   [120]  [9.0]
[K]   [110]  [9.5]

Это отсортировано по PAR2 - меньше означает лучший результат.Мне нужно найти лучший результат SUM PAR2 из 3 строк , где сумма PAR1 составляет минимум 350 (как минимум 350).Например:

  • комбинация A + B + C дает лучший результат суммы PAR2 (0,5 + 1,5 + 2,0 = 4,0), но сумма PAR1: 110+ 105 + 120 = 335 <(350) - условие не в порядке, не может использовать результат, </li>
  • комбинация A + B + D дает результат суммы PAR2 (0,5+ 1,5 + 3,0 = 5,0), но сумма PAR1: 110 + 105 + 130 = 345 <(350) - условие не в порядке, нельзя использовать результат </li>
  • комбинация A + B + E дает результат суммы PAR2 (0,5 + 1,5 + 5,5 = 7,5), но сумма PAR1: 110 + 105 + 115 = 330 <(350) - условие не в порядке, нельзя использовать результат </li>
  • комбинация A + B + F дает результат суммы PAR2 (0,5 + 1,5 + 6,5 = 8,5), но сумма PAR1: 110 + 105 + 130 = 345 <(350) - условие нехорошо, не могу использовать результат (...) </li>
  • комбинация B + C + D дает результат суммы PAR2 (1,5 + 2,0 + 3,0 = 6,5) и суммы PAR1: 105 + 120 + 130 = 355> (350) - условие в порядке !, у нас есть победитель с лучшим результатом 6,5

Это приложение ASP.NET,поэтому я попробовалчтобы получить таблицу из базы данных и использовать код VB для получения результата, но это «ручная» работа с использованием FOR..NEXT LOOP, занимает некоторое время.Так что это не очень хороший вариант для таких вычислений, а также слишком медленный.

Мне интересно, есть ли лучший плавный и умный код SQL для получения результата непосредственно из SQL-запроса.Может быть, некоторые продвинутые математические функции?Есть идеи?Заранее спасибо.


Я провел тест с использованием решения forpas, и да, он работает очень хорошо.Но это заняло много времени, когда я добавил много условий WHERE, потому что исходная таблица очень большая.Поэтому я попытаюсь найти решение для использования временных таблиц в функции (не процедур).Спасибо всем за ваши ответы.

forpas, отдельное спасибо также за пример и объяснение, тем самым вы позволили мне быстро понять вашу идею - это уровень мастера;)

Ответы [ 3 ]

1 голос
/ 13 мая 2019

Вы можете использовать двойное внутреннее самостоятельное соединение, например:

select top 1 * from tablename t1
inner join tablename t2 on t2.id > t1.id
inner join tablename t3 on t3.id > t2.id
where t1.par1 + t2.par1 + t3.par1 >= 350
order by t1.par2 + t2.par2 + t3.par2

См. Демоверсию .Результаты:

> ID | PAR1 | PAR2 | ID | PAR1 | PAR2 | ID | PAR1 | PAR2
> :- | ---: | :--- | :- | ---: | :--- | :- | ---: | :---
> A  |  110 | 0.5  | C  |  120 | 2.0  | D  |  130 | 3.0 

Итак, победитель - A + C + D , потому что:

110 + 120 + 130 = 360 >= 350 

, а сумма PAR2 равна

0.5 + 2.0 + 3.0 = 5.5

что является минимальным

0 голосов
/ 13 мая 2019

Вы можете попытаться соединить таблицу вместе с самим собой три раза. Таким образом, вы получите всю комбинацию трех рядов, развернутых в одной строке, что позволит вам применять требуемые условия и выбирать максимальное значение.

select  t1.ID, t2.ID, t3.ID, t1.PAR2 + t2.PAR2 + t3.PAR2
from    yourTable t1
cross join
        yourTable t2
cross join
        yourTable t3
where   t1.ID < t2.ID and t2.ID < t3.ID and
        t1.PAR1 + t2.PAR1 + t3.PAR1 >= 350
order by t1.PAR2 + t2.PAR2 + t3.PAR2 ASC

Хотя это решение технически должно работать, перекрестное объединение таблиц не является идеальным с точки зрения производительности, особенно в случае многократного выполнения. Если размер таблицы со временем будет расти, и у вас есть возможность применить вычисления на уровне кода, я думаю, что было бы целесообразно сделать это.

Редактировать

Изменено предложение where, включая предложение Серга

0 голосов
/ 13 мая 2019

Проверьте это. Я чувствую, что это точно или близко к вашему требованию -

WITH CTE (ID,PAR1,PAR2)
AS
(
    SELECT 'A',110,0.5  UNION ALL
    SELECT 'B',105,1.5  UNION ALL
    SELECT 'C',120,2.0  UNION ALL
    SELECT 'D',130,3.0  UNION ALL
    SELECT 'E',115,5.5  UNION ALL
    SELECT 'F',130,6.5  UNION ALL
    SELECT 'G',120,7.0  UNION ALL
    SELECT 'H',110,7.5  UNION ALL
    SELECT 'I',105,8.0  UNION ALL
    SELECT 'J',120,9.0  UNION ALL
    SELECT 'K',110,9.5
)
SELECT B.AID,B.BID,B.CID,SUM_P2,SUM_P1
(
    SELECT * , ROW_NUMBER() OVER (PARTITION BY CHAR_SUM ORDER BY CHAR_SUM) CS
    FROM 
    (
        SELECT ASCII(A.ID) + ASCII(B.ID)+ASCII(C.ID) CHAR_SUM,
        A.ID AID,B.ID BID,C.ID CID,
        (A.PAR2+B.PAR2+C.PAR2) AS SUM_P2,
        (A.PAR1+B.PAR1+C.PAR1) AS SUM_P1
        FROM CTE A
        CROSS APPLY CTE B
        CROSS APPLY CTE C
        WHERE A.ID <> B.ID AND A.ID <> C.ID AND B.ID <> C.ID
        AND (A.PAR1+B.PAR1+C.PAR1) >= 350
    ) A
)B
WHERE CS = 1
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...