admin管理员组文章数量:1024725
I have two tables:
- The first is used to determine the percentage for the supplier according to
salesperson
,customer
, andquantity
. - The second is used to determine the price according to
customer
,article
andquantity
.customer
is not unique in either tables.
I have created a DBFiddle here.
In the fiddle you can see the results of the join on customer
, but it is not entirely correct as some rows are not allowed.
For example the 2nd one: if prices.qty
is 50+ then the percentage.qty
is >20, and therefore only the second percentage, 0.50 is allowed, not the first (0.25).
The table info:
[percentages]
supplier | salesperson | customer | qty | percent |
---|---|---|---|---|
John | Anne | David | 0 | 0.25 |
John | Anne | David | 20 | 0.50 |
John | Anne | Mary | 0 | 0.25 |
Paul | Andrew | David | 0 | 0.25 |
I have two tables:
- The first is used to determine the percentage for the supplier according to
salesperson
,customer
, andquantity
. - The second is used to determine the price according to
customer
,article
andquantity
.customer
is not unique in either tables.
I have created a DBFiddle here.
In the fiddle you can see the results of the join on customer
, but it is not entirely correct as some rows are not allowed.
For example the 2nd one: if prices.qty
is 50+ then the percentage.qty
is >20, and therefore only the second percentage, 0.50 is allowed, not the first (0.25).
The table info:
[percentages]
supplier | salesperson | customer | qty | percent |
---|---|---|---|---|
John | Anne | David | 0 | 0.25 |
John | Anne | David | 20 | 0.50 |
John | Anne | Mary | 0 | 0.25 |
Paul | Andrew | David | 0 | 0.25 |
[prices]
Customer | article | qty | price |
---|---|---|---|
David | X | 0 | 50 |
David | X | 50 | 40 |
David | Y | 0 | 50 |
Mary | X | 0 | 50 |
Mary | Y | 0 | 55 |
Quantity (qty) is the lower cut-off of a quantity: it gives the minimum nr of sold items to which the percentage (in the 1st table) or the price (in the 2nd table) can be applied. For exampl,e given John, Anne and David as supplier, salesperson and customer, if the nr of sold item is 0-19 then the percentage is 0.25, if sold items are 20 or more then the percentage is 0.50. Similarly for price: given David and X as customer and article, the price is 50 if the nr of sold items is 0 to 49, otherwise if more the price is 40.
These are the expected results:
[Results]
supplier | salesperson | customer | qty | percent | article | qty | price |
---|---|---|---|---|---|---|---|
John | Anne | David | 0 | 0.25 | X | 0 | 50 |
John | Anne | David | 20 | 0.50 | X | 0 | 50 |
John | Anne | David | 20 | 0.50 | X | 50 | 40 |
John | Anne | David | 0 | 0.25 | Y | 0 | 50 |
John | Anne | David | 20 | 0.50 | Y | 0 | 50 |
John | Anne | Mary | 0 | 0.25 | X | 0 | 50 |
John | Anne | Mary | 0 | 0.25 | Y | 0 | 55 |
Paul | Andrew | David | 0 | 0.25 | X | 0 | 50 |
Paul | Andrew | David | 0 | 0.25 | X | 50 | 40 |
Paul | Andrew | David | 0 | 0,25 | Y | 0 | 50 |
That is, I have all the possible combinations of percentages and prices that make sense. For example, I do not have a row where percentages.qty is 0 and price.qty is 50, if there is a different percentage for a qty higher than 50.
Share Improve this question edited Nov 22, 2024 at 9:02 Davide asked Nov 18, 2024 at 15:40 DavideDavide 114 bronze badges 11- Please provide a Minimal Reproducible Example For questions regarding sql this typically involves, table structure, example data and queries as TEXT and not as image. Additionally to providing these informations within the question, you are also welcome to create a runable fiddle of your problem. Finally, please also tag the respective DBMS you are using ... – derpirscher Commented Nov 18, 2024 at 15:46
- Here is a fiddle to help us collaborate on a solution dbfiddle.uk/2SxOdvL5 – Bart McEndree Commented Nov 18, 2024 at 15:50
- @BartMcEndree What in the question tells you OP is using MS SQL Server? Besides that, I'm not even sure what OP is actually asking ... – derpirscher Commented Nov 18, 2024 at 15:52
- 3 @BartMcEndree No, the correct way would be to ask for clarification, and vote/flag to close as "Needs details or clarity" or needing "Debugging details". – Mark Rotteveel Commented Nov 18, 2024 at 16:10
- 1 Please read this >15 year old article: bad habits to kick using old style joins – Luuk Commented Nov 20, 2024 at 11:48
1 Answer
Reset to default 0Your description is still confusing. However, your expected results omit only one candidate row from a join between the two tables on customer
. If the qty
values for each of the two tables are converted to quantity ranges, that omitted row seems to be the only row where the ranges do not overlap.
Based on that, I would propose the following logic:
- Calculate quantity ranges for each row of both tables. The original
qty
becomes the lower bound andLEAD(qty) - 1
becomes the upper bound. (We can use a closed interval, since we are dealing with discrete integer values.) IfLEAD(qty) - 1
is null (no value found) the upper end of the range is unbounded. - Join these intermediate results on
customer
. - Further limit the join to overlapping ranges. A standard test for overlapping closed intervals is
lower1 <= upper2 AND lower2 <= upper1
. To handle null (unbounded) upper range values, we can substitute a large value usingISNULL()
.
The result would be something like:
WITH PercentagesQtyRange AS (
SELECT
supplier, salesperson, customer, qty, [percent],
qty AS QtyLower,
LEAD(Qty) OVER(PARTITION BY supplier, salesperson, customer ORDER BY qty)
- 1 AS QtyUpper
FROM percentages
),
PricesQtyRange AS (
SELECT
customer, article, qty, price, qty AS QtyLower,
LEAD(Qty) OVER(PARTITION BY customer, article ORDER BY qty)
- 1 AS QtyUpper
FROM prices
)
SELECT *,
GREATEST(PER.QtyLower, PR.QtyLower) AS OverlapLower,
LEAST(PER.QtyUpper, PR.QtyUpper) AS OverlapUpper
FROM PercentagesQtyRange PER
JOIN PricesQtyRange PR
ON PR.customer = PER.customer
AND PR.QtyLower <= ISNULL(PER.QtyUpper, 999999999) -- or ISNULL(..., PR.QtyLower)
AND PER.QtyLower <= ISNULL(PR.QtyUpper, 999999999) -- or ISNULL(..., PER.QtyLower)
ORDER BY
PER.customer, PER.supplier, PER.salesperson, PR.article,
OverlapLower
LEAD()
function is a SQL window function. Window functions allow access to and calculations on values from other rows in the results. using OVER(...)
to define the scope of the rows that they access without using a GROUP BY
. Typically they are aggregation functions like COUNT()
, SUM()
or MAX()
, but may also be any of LEAD()
, LAG()
, FIRST_VALUE()
, and LAST_VALUE()
that are defined specifically as a window function. The OVER(...)
clause defines the scope of the rows being accessed. The PARTITION BY
specifies the logical grouping of data, and limits the window function to just one group of rows. The ORDER BY
defines an ordering within each partition, for those functions (like LEAD()
) that depend on order. An OVER()
clause may also contain a ROWS ...
or RANGE ...
clause to include multiple rows in the window function calculation, but we don't need that for the LEAD()
function. Wee only need to access one value on one following row.
The CTE calculated ranges are as follows:
supplier | salesperson | customer | qty | percent | QtyLower | QtyUpper |
---|---|---|---|---|---|---|
John | Anne | David | 0 | 0.25 | 0 | 19 |
John | Anne | David | 20 | 0.5 | 20 | null |
Paul | Andrew | David | 0 | 0.25 | 0 | null |
John | Anne | Mary | 0 | 0.25 | 0 | null |
customer | article | qty | price | QtyLower | QtyUpper |
---|---|---|---|---|---|
David | X | 0 | 50 | 0 | 49 |
David | X | 50 | 40 | 50 | null |
David | Y | 0 | 50 | 0 | null |
Mary | X | 0 | 50 | 0 | null |
Mary | Y | 0 | 55 | 0 | null |
Final Results:
supplier | salesperson | customer | qty | percent | QtyLower | QtyUpper | customer | article | qty | price | QtyLower | QtyUpper | OverlapLower | OverlapUpper |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
John | Anne | David | 0 | 0.25 | 0 | 19 | David | X | 0 | 50 | 0 | 49 | 0 | 19 |
John | Anne | David | 20 | 0.5 | 20 | null | David | X | 0 | 50 | 0 | 49 | 20 | 49 |
John | Anne | David | 20 | 0.5 | 20 | null | David | X | 50 | 40 | 50 | null | 50 | null |
John | Anne | David | 0 | 0.25 | 0 | 19 | David | Y | 0 | 50 | 0 | null | 0 | 19 |
John | Anne | David | 20 | 0.5 | 20 | null | David | Y | 0 | 50 | 0 | null | 20 | null |
Paul | Andrew | David | 0 | 0.25 | 0 | null | David | X | 0 | 50 | 0 | 49 | 0 | 49 |
Paul | Andrew | David | 0 | 0.25 | 0 | null | David | X | 50 | 40 | 50 | null | 50 | null |
Paul | Andrew | David | 0 | 0.25 | 0 | null | David | Y | 0 | 50 | 0 | null | 0 | null |
John | Anne | Mary | 0 | 0.25 | 0 | null | Mary | X | 0 | 50 | 0 | null | 0 | null |
John | Anne | Mary | 0 | 0.25 | 0 | null | Mary | Y | 0 | 55 | 0 | null | 0 | null |
Omitted row (customer matches, but ranges do not overlap):
supplier | salesperson | customer | qty | percent | QtyLower | QtyUpper | customer | article | qty | price | QtyLower | QtyUpper | OverlapLower | OverlapUpper |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
John | Anne | David | 0 | 0.25 | 0 | 19 | David | X | 50 | 40 | 50 | null | 50 | 19 |
See this db<>fiddle for a demo.
The overlap range calculations in the result are for information only, but they do make use of the LEAST()
and GREATEST()
functions introduced in SQL Server 2022. For earlier SQL Server versions, you can use an IIF()
equivalent together with COALESCE()
to handle null upper bound values.
IIF(PER.QtyLower > PR.QtyLower, PER.QtyLower, PR.QtyLower) AS OverlapLower,
COALESCE(IIF(PER.QtyUpper < PR.QtyUpper, PER.QtyUpper, PR.QtyUpper), PER.QtyUpper, PR.QtyUpper) AS OverlapUpper
See this db<>fiddle.
I have two tables:
- The first is used to determine the percentage for the supplier according to
salesperson
,customer
, andquantity
. - The second is used to determine the price according to
customer
,article
andquantity
.customer
is not unique in either tables.
I have created a DBFiddle here.
In the fiddle you can see the results of the join on customer
, but it is not entirely correct as some rows are not allowed.
For example the 2nd one: if prices.qty
is 50+ then the percentage.qty
is >20, and therefore only the second percentage, 0.50 is allowed, not the first (0.25).
The table info:
[percentages]
supplier | salesperson | customer | qty | percent |
---|---|---|---|---|
John | Anne | David | 0 | 0.25 |
John | Anne | David | 20 | 0.50 |
John | Anne | Mary | 0 | 0.25 |
Paul | Andrew | David | 0 | 0.25 |
I have two tables:
- The first is used to determine the percentage for the supplier according to
salesperson
,customer
, andquantity
. - The second is used to determine the price according to
customer
,article
andquantity
.customer
is not unique in either tables.
I have created a DBFiddle here.
In the fiddle you can see the results of the join on customer
, but it is not entirely correct as some rows are not allowed.
For example the 2nd one: if prices.qty
is 50+ then the percentage.qty
is >20, and therefore only the second percentage, 0.50 is allowed, not the first (0.25).
The table info:
[percentages]
supplier | salesperson | customer | qty | percent |
---|---|---|---|---|
John | Anne | David | 0 | 0.25 |
John | Anne | David | 20 | 0.50 |
John | Anne | Mary | 0 | 0.25 |
Paul | Andrew | David | 0 | 0.25 |
[prices]
Customer | article | qty | price |
---|---|---|---|
David | X | 0 | 50 |
David | X | 50 | 40 |
David | Y | 0 | 50 |
Mary | X | 0 | 50 |
Mary | Y | 0 | 55 |
Quantity (qty) is the lower cut-off of a quantity: it gives the minimum nr of sold items to which the percentage (in the 1st table) or the price (in the 2nd table) can be applied. For exampl,e given John, Anne and David as supplier, salesperson and customer, if the nr of sold item is 0-19 then the percentage is 0.25, if sold items are 20 or more then the percentage is 0.50. Similarly for price: given David and X as customer and article, the price is 50 if the nr of sold items is 0 to 49, otherwise if more the price is 40.
These are the expected results:
[Results]
supplier | salesperson | customer | qty | percent | article | qty | price |
---|---|---|---|---|---|---|---|
John | Anne | David | 0 | 0.25 | X | 0 | 50 |
John | Anne | David | 20 | 0.50 | X | 0 | 50 |
John | Anne | David | 20 | 0.50 | X | 50 | 40 |
John | Anne | David | 0 | 0.25 | Y | 0 | 50 |
John | Anne | David | 20 | 0.50 | Y | 0 | 50 |
John | Anne | Mary | 0 | 0.25 | X | 0 | 50 |
John | Anne | Mary | 0 | 0.25 | Y | 0 | 55 |
Paul | Andrew | David | 0 | 0.25 | X | 0 | 50 |
Paul | Andrew | David | 0 | 0.25 | X | 50 | 40 |
Paul | Andrew | David | 0 | 0,25 | Y | 0 | 50 |
That is, I have all the possible combinations of percentages and prices that make sense. For example, I do not have a row where percentages.qty is 0 and price.qty is 50, if there is a different percentage for a qty higher than 50.
Share Improve this question edited Nov 22, 2024 at 9:02 Davide asked Nov 18, 2024 at 15:40 DavideDavide 114 bronze badges 11- Please provide a Minimal Reproducible Example For questions regarding sql this typically involves, table structure, example data and queries as TEXT and not as image. Additionally to providing these informations within the question, you are also welcome to create a runable fiddle of your problem. Finally, please also tag the respective DBMS you are using ... – derpirscher Commented Nov 18, 2024 at 15:46
- Here is a fiddle to help us collaborate on a solution dbfiddle.uk/2SxOdvL5 – Bart McEndree Commented Nov 18, 2024 at 15:50
- @BartMcEndree What in the question tells you OP is using MS SQL Server? Besides that, I'm not even sure what OP is actually asking ... – derpirscher Commented Nov 18, 2024 at 15:52
- 3 @BartMcEndree No, the correct way would be to ask for clarification, and vote/flag to close as "Needs details or clarity" or needing "Debugging details". – Mark Rotteveel Commented Nov 18, 2024 at 16:10
- 1 Please read this >15 year old article: bad habits to kick using old style joins – Luuk Commented Nov 20, 2024 at 11:48
1 Answer
Reset to default 0Your description is still confusing. However, your expected results omit only one candidate row from a join between the two tables on customer
. If the qty
values for each of the two tables are converted to quantity ranges, that omitted row seems to be the only row where the ranges do not overlap.
Based on that, I would propose the following logic:
- Calculate quantity ranges for each row of both tables. The original
qty
becomes the lower bound andLEAD(qty) - 1
becomes the upper bound. (We can use a closed interval, since we are dealing with discrete integer values.) IfLEAD(qty) - 1
is null (no value found) the upper end of the range is unbounded. - Join these intermediate results on
customer
. - Further limit the join to overlapping ranges. A standard test for overlapping closed intervals is
lower1 <= upper2 AND lower2 <= upper1
. To handle null (unbounded) upper range values, we can substitute a large value usingISNULL()
.
The result would be something like:
WITH PercentagesQtyRange AS (
SELECT
supplier, salesperson, customer, qty, [percent],
qty AS QtyLower,
LEAD(Qty) OVER(PARTITION BY supplier, salesperson, customer ORDER BY qty)
- 1 AS QtyUpper
FROM percentages
),
PricesQtyRange AS (
SELECT
customer, article, qty, price, qty AS QtyLower,
LEAD(Qty) OVER(PARTITION BY customer, article ORDER BY qty)
- 1 AS QtyUpper
FROM prices
)
SELECT *,
GREATEST(PER.QtyLower, PR.QtyLower) AS OverlapLower,
LEAST(PER.QtyUpper, PR.QtyUpper) AS OverlapUpper
FROM PercentagesQtyRange PER
JOIN PricesQtyRange PR
ON PR.customer = PER.customer
AND PR.QtyLower <= ISNULL(PER.QtyUpper, 999999999) -- or ISNULL(..., PR.QtyLower)
AND PER.QtyLower <= ISNULL(PR.QtyUpper, 999999999) -- or ISNULL(..., PER.QtyLower)
ORDER BY
PER.customer, PER.supplier, PER.salesperson, PR.article,
OverlapLower
LEAD()
function is a SQL window function. Window functions allow access to and calculations on values from other rows in the results. using OVER(...)
to define the scope of the rows that they access without using a GROUP BY
. Typically they are aggregation functions like COUNT()
, SUM()
or MAX()
, but may also be any of LEAD()
, LAG()
, FIRST_VALUE()
, and LAST_VALUE()
that are defined specifically as a window function. The OVER(...)
clause defines the scope of the rows being accessed. The PARTITION BY
specifies the logical grouping of data, and limits the window function to just one group of rows. The ORDER BY
defines an ordering within each partition, for those functions (like LEAD()
) that depend on order. An OVER()
clause may also contain a ROWS ...
or RANGE ...
clause to include multiple rows in the window function calculation, but we don't need that for the LEAD()
function. Wee only need to access one value on one following row.
The CTE calculated ranges are as follows:
supplier | salesperson | customer | qty | percent | QtyLower | QtyUpper |
---|---|---|---|---|---|---|
John | Anne | David | 0 | 0.25 | 0 | 19 |
John | Anne | David | 20 | 0.5 | 20 | null |
Paul | Andrew | David | 0 | 0.25 | 0 | null |
John | Anne | Mary | 0 | 0.25 | 0 | null |
customer | article | qty | price | QtyLower | QtyUpper |
---|---|---|---|---|---|
David | X | 0 | 50 | 0 | 49 |
David | X | 50 | 40 | 50 | null |
David | Y | 0 | 50 | 0 | null |
Mary | X | 0 | 50 | 0 | null |
Mary | Y | 0 | 55 | 0 | null |
Final Results:
supplier | salesperson | customer | qty | percent | QtyLower | QtyUpper | customer | article | qty | price | QtyLower | QtyUpper | OverlapLower | OverlapUpper |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
John | Anne | David | 0 | 0.25 | 0 | 19 | David | X | 0 | 50 | 0 | 49 | 0 | 19 |
John | Anne | David | 20 | 0.5 | 20 | null | David | X | 0 | 50 | 0 | 49 | 20 | 49 |
John | Anne | David | 20 | 0.5 | 20 | null | David | X | 50 | 40 | 50 | null | 50 | null |
John | Anne | David | 0 | 0.25 | 0 | 19 | David | Y | 0 | 50 | 0 | null | 0 | 19 |
John | Anne | David | 20 | 0.5 | 20 | null | David | Y | 0 | 50 | 0 | null | 20 | null |
Paul | Andrew | David | 0 | 0.25 | 0 | null | David | X | 0 | 50 | 0 | 49 | 0 | 49 |
Paul | Andrew | David | 0 | 0.25 | 0 | null | David | X | 50 | 40 | 50 | null | 50 | null |
Paul | Andrew | David | 0 | 0.25 | 0 | null | David | Y | 0 | 50 | 0 | null | 0 | null |
John | Anne | Mary | 0 | 0.25 | 0 | null | Mary | X | 0 | 50 | 0 | null | 0 | null |
John | Anne | Mary | 0 | 0.25 | 0 | null | Mary | Y | 0 | 55 | 0 | null | 0 | null |
Omitted row (customer matches, but ranges do not overlap):
supplier | salesperson | customer | qty | percent | QtyLower | QtyUpper | customer | article | qty | price | QtyLower | QtyUpper | OverlapLower | OverlapUpper |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
John | Anne | David | 0 | 0.25 | 0 | 19 | David | X | 50 | 40 | 50 | null | 50 | 19 |
See this db<>fiddle for a demo.
The overlap range calculations in the result are for information only, but they do make use of the LEAST()
and GREATEST()
functions introduced in SQL Server 2022. For earlier SQL Server versions, you can use an IIF()
equivalent together with COALESCE()
to handle null upper bound values.
IIF(PER.QtyLower > PR.QtyLower, PER.QtyLower, PR.QtyLower) AS OverlapLower,
COALESCE(IIF(PER.QtyUpper < PR.QtyUpper, PER.QtyUpper, PR.QtyUpper), PER.QtyUpper, PR.QtyUpper) AS OverlapUpper
See this db<>fiddle.
本文标签: sql serverSQL query finding percentages and pricesStack Overflow
版权声明:本文标题:sql server - SQL query: finding percentages and prices - Stack Overflow 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://it.en369.cn/questions/1745610814a2158993.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论