I'm stuck in select query to get best match from table like this:
Name | A | B | TotalA | TotalB |
---|---|---|---|---|
Name1 | 10 | 1 | 10 | 1 |
Name2 | 15 | 2 | 25 | 3 |
Name3 | 20 | 3 | 45 | 6 |
Name4 | 25 | 4 | 70 | 10 |
Name5 | 30 | 5 | 100 | 15 |
Name6 | 35 | 6 | 135 | 21 |
Name7 | 51 | 7 | 175 | 28 |
..... | .... | ...... | ...... | ...... |
..... | .... | ...... | ...... | ...... |
I'm trying to find best 6 records with the lowest sum of B, but there's additional condition that sum of A need to be higher than 150.
This table is sorted by B, and the best results is on the top. So sum of B from 6 top results is 21, but in this case sum of A is 135 which is lower than 150. So record nb 6 should be excluded and the next nb 7 should be included. So the best result is sum of B = 22 from records 1-5 and 7, while sum of A is 151, which is enough.
I used rank function with partition and order but every time query returned 0 records. Is there any possibility to exclude this 6th record and take 7 to the result, so sum of A will be > 150?
CodePudding user response:
This seems to be a variation on the knapsack problem, which is NP-Complete, and the easiest (to code) solution is just brute-forcing every permutation.
Given that you have a fixed number of items, you can do this by self-joining
SELECT TOP (1)
*
FROM Items i1
JOIN Items i2 ON i2.Name > i1.Name
JOIN Items i3 ON i3.Name > i2.Name
JOIN Items i4 ON i4.Name > i3.Name
JOIN Items i5 ON i5.Name > i4.Name
JOIN Items i6 ON i6.Name > i5.Name
CROSS APPLY (VALUES (
i1.A i2.A i3.A i4.A i5.A i6.A,
i1.B i2.B i3.B i4.B i5.B i6.B
)) v(TotalA, TotalB)
WHERE TotalA > 150
ORDER BY
TotalB;
Name
in this case is just a proxy for some unique column
There may be more efficient solutions, depending on your exact problem.