Two Tables Relationship based on conditions - sql

I am stuck in the following situations.
I have two tables Products and Postage:
Postage table
+----------------------------------+
| Weight_GM | Postal Charges ($) |
+----------------------------------+
| 20 | 1 |
| 40 | 1.5 |
| 50 | 1.7 |
+----------------------------------+
Products table
+-------------------------------+
| SKU | Title | Weight_GM |
+-------------------------------+
| ABC | Shose | 17 |
| JKL | Camera | 27 |
| XYZ | IPad | 48 |
+-------------------------------+
I want to create a relationship to take appropriate postal charge from Postage table based on the Weight defined in both tables.
The desired result would be like this:
+---------------------------------------------------+
| SKU | Title | Weight_GM | Postal Charges |
+---------------------------------------------------+
| ABC | Shose | 17 | 1 |
| JKL | Camera | 27 | 1.5 |
| XYZ | IPad | 48 | 1.7 |
+---------------------------------------------------+
Note: I have been through many similar questions but there was not solution to my problem.
Thanks in advance.

This should work -- just use GROUP BY and MIN:
SELECT DISTINCT Pr.SKU, Pr.Title, Pr.Weight_GM, MIN(PO.Postal_Charges) as PO_Charges
FROM Products Pr
JOIN Postage Po ON Pr.Weight_GM <= Po.Weight_GM
GROUP BY Pr.SKU, Pr.Title, Pr.Weight_GM
And the Fiddle.

This should do it
;With WeightNumber AS
(
SELECT Weight_GM, Postal_Charge, ROW_NUMBER() OVER (ORDER BY Weight_GM) AS Num
FROM Postage
)
WeightRange AS
(
SELECT ISNULL(Prec.Weight_GM - 1, 0) AS START_WEIGHT, CurrentRow.Weight_GM AS END_WEIGHT, CurrentRow.Postal_Charge
FROM WeightNumber CurrentRow
LEFT JOIN WeightNumber Prec
ON Prec.Num=CurrentRow.Num - 1
)
SELECT *
FROM Products p
JOIN WeightRange w
ON p.Weight_GM BETWEEN w.START_WEIGHT AND w.END_WEIGHT

You can do this with the following code
The inner join will join the two tables with the key Weight_GM
select a.SKU, b.Postal Charges from Products a
inner join Postage b on a.Weight_GM = b.Weight_GM

try this:
select SKU,Title,prds.Weight_GM b,pst.Weight_GM a,Postal_charges
from Postage pst ,Products prds
where prds.Weight_GM < pst.Weight_GM
group by prds.Weight_GM;
here's the demo : demo link

Related

SQL Group By and Join based on a weird client table

I have 3 tables that I want to join together and group it to get client membership info. My code works for grouping the base table together but it breaks at the join part and I can't figure out why.
BASE TABLE : sales_detail
+-------+-----------+-----------+-----------------------------------------+
| order_date | transaction_id| product_cost | payment_type | country
+-------+-----------+-----------+------------------------------------------+
| 10/1 | 12345 | 20 | mastercard | usa
| 10/1 | 12345 | 50 | mastercard | usa
| 10/5 | 82456 | 50 | mastercard | usa
| 10/9 | 64789 | 30 | visa | canada
| 10/15 | 08546 | 20 | mastercard | usa
| 10/15 | 08546 | 90 | mastercard | usa
| 10/17 | 65898 | 50 | mastercard | usa
+-------+-----------+-----------+-------------------------------------+
table : client_information
+-------+-----------+-----------+-------------------+
| other_id | client_Type | item
+-------+-----------+-----------+----------+
| 112341 | new | hola |
| 112341 | old | mango |
| 145634 | old | pine |
| 879547 | old | vip |
| 745688 | new | unio |
| 745688 | old | dog |
| 147899 | new | cat |
| 124589 | new | amigo |
+-------+-----------+-----------+-----------+
table : connector
+-------+-----------+-----------+-------------------+
| transaction_ID | other_id | item
+-------+-----------+-----------+----------+
| 12345 | 112341 | hola |
| 82456 | 145634 | pine |
| 08157 | 879547 | unio |
| 08546 | 745688 | dog |
| 65898 | 147899 | cat |
| 06587 | 124589 | amigo |
+-------+-----------+-----------+-----------+
**I want the output to look something like this: **
IDEAL OUTPUT
+-------+-----------+-----------+--------------------------------+
| order_date | transaction_ID | product_cost | client_Type|
+-------+-----------+-----------+--------------------------------+
| 10/1 | 12345 | 70 | new |
| 10/5 | 82456 | 70 | old |
| 10/15 | 08546 | 110 | old |
| 10/17 | 65898 | 50 | new |
+-------+-----------+-----------+----------------------------------+
**i am trying to join my base table to the connector table by transaction ID to get other_id and items to match to client_type **
This is the code i used but it failed to compile after adding in left joins :
select t1.transaction_id, sum(t1.product_cost), t1.order_date, t3.client_type
from sales_detail t1
left join (select DISTINCT transaction_ID, other_id, fruits from connector) t2
ON t1.transaction_ID=t2.transaction_ID
left join (select DISTINCT order_id, client_type, fruits from client information) t3
ON t2.other_id=t3.other_id and t2.item=t3.item
where t1.payment_type='mastercard' and t1.order_Date between '2020-10-01' and'2020-10-31'
and country != 'canada'
GROUP BY t1.transaction_id, t1.order_date, t3.client_type;
Thanks in advance! I am a beginner so still learning the ins and outs of sql! (am using hive)
I think that's joins and aggregation. For more efficiency, you can pre-aggregate in a subquery, then join:
select sd.*, ci.client_type
from (
select order_date, transaction_id, sum(product_cost) product_cost
from sales_detail
where
payment_type = 'mastercard'
and order_date >= '2020-10-01'
and order_date < '2020-11-01'
and country <> 'canada'
group by order_date, transaction_id
) sd
inner join connector c on c.transaction_id = sd.transaction_id
inner join client_information ci on ci.other_id = c.other_id
Note that I rewrote the filter on order_date to use half-open intervals rather than between. This properly handles the case when your dates have a time portion.
From what I have understood, your code works although not as you would like using an INNER JOIN and it fails to add a LEFT JOIN. I think what happens is a failure due to the NULL elements. to add a NULL element and not get an error, you have to use some function that changes the NULL value to 0 .
One such function is the ISNULL(yourColumn, 0) function of T-SQL.The documentation.
I can see that in result table you only need clients who used mastercard, so you should use inner join there so only those client who used mastercard will be considered. While the remaining query is okay i guess, but main problem was the join on client information.
I think on the answer with GMB you also need to join on item column otherwise you will get multiple rows output.
select sd.*, ci.client_type
from (
select order_date, transaction_id, sum(product_cost) product_cost
from sales_detail
group by order_date, transaction_id
) sd
inner join connector c on c.transaction_id = sd.transaction_id
inner join client_information ci on ci.other_id = c.other_id and ci.item = c.item
Just modify with your filters and you should be sorted.

Using CASE in WHERE clause

I have 2 tables that needs to be joined based on couple of parameters. One of the parameters is year. One table contains the current year but another year doesn't contain current year, so it must use the latest year and matched with other parameters.
Example
Product
-------------------------------------------------------------------------------
| product_id | category_id | sub_category_id | product_year | amount |
-------------------------------------------------------------------------------
| 504 | I | U | 2020 | 400 |
| 510 | I | U | 2019 | 100 |
| 528 | I | U | 2019 | 150 |
| 540 | I | U | 2018 | 1000 |
Discount
-----------------------------------------------------------------------------
| discount_year | category_id | sub_category_id | discount |
-----------------------------------------------------------------------------
| 2018 | I | U | 0.15 |
| 2017 | I | U | 0.35 |
| 2016 | I | U | 0.50 |
Output
-----------------------------------------------------------------------------
| product_id | category_id | sub_category_id | product_year | discount_year |
-----------------------------------------------------------------------------
| 504 | I | U | 2020 | 2018 |
| 510 | I | U | 2019 | 2018 |
| 528 | I | U | 2019 | 2018 |
| 540 | I | U | 2018 | 2017 |
The discount is always gotten from one year behind but if those rates aren't available, then it would keep going back a year until available.
I have tried the following:
SELECT
product_year, a.product_id, a.category_id, a.sub_category_id,
discount_year, amount, discount
FROM
Product a
INNER JOIN
Discount b ON a.category_id = b.category_id
AND a.sub_category_id = b.sub_category_id
AND product_ year = CASE
WHEN discount_year + 1 = product_year
THEN discount_year + 1
WHEN discount_year + 2 = product_year
THEN discount_year + 2
WHEN discount_year + 3 = product_year
THEN discount_year + 3
END
WHERE
product = 540
This return the following output:
--------------------------------------------------------------------------------------------------
| product_year | product_id | category_id | sub_category_id | discount_year | amount | discount |
--------------------------------------------------------------------------------------------------
| 2016 | 540 | I | U | 2017 | 1000 | 0.50 |
| 2017 | 540 | I | U | 2017 | 1000 | 0.35 |
Any help will be appreciated.
You can use OUTER APPLY and a subquery. In the subquery select the row with the maximum discount_year, that is less the product_year using TOP and ORDER BY.
SELECT p.product_year,
p.product_id,
p.category_id,
p.sub_category_id,
d.discount_year,
p.amount,
d.discount
FROM product p
OUTER APPLY (SELECT TOP 1
*
FROM discount d
WHERE d.category_id = p.category_id
AND d.sub_category_id = p.sub_category_id
AND d.discount_year < p.product_year
ORDER BY d.discount_year DESC) d;
instead of a CASE expression you can use a sub-query to select the TOP 1 related
discount_year that is less than your product_year, ORDER BY discount_year ASC.
Create a product to discount mapping using a CTE first. This contains the discount year pulled from discount table for every product year in the product table and corresponding product_id. Following this, you can easily join with relevant tables to get results and eliminate any nulls as needed
Simplified query.
;WITH disc_prod_mapper
AS
(
SELECT product_id, product_year,(SELECT MAX(discount_year) FROM #Discount b WHERE discount_year < product_year AND a.category_id = b.category_id AND a.sub_category_id = b.sub_category_id ) AS discount_year
FROM Product a
)
SELECT a.product_year, c.discount_year, a.amount, c.discount
FROM Product a
LEFT JOIN disc_prod_mapper b ON a.product_id = b.product_id
LEFT JOIN Discount c ON b.discount_year = c.discount_year

SQL Query to Return SUMS and Count Ordered by Date

I have the two following tables:
Table 1
Table 2
What I want to do is to have a query that returns a SUM of PIT_VALORTOTAL, PIT_VOLUME and a count of PED_IDPESSOA per date. What I have so far is:
SELECT SUM(PIT_VALORTOTAL) AS VALORTOTAL, SUM(PIT_VOLUME) AS VOLUME, COUNT(DISTINCT PED_IDPESSOA) AS PESSOA FROM PEDIDOS_ITENS INNER JOIN PEDIDOS ON PIT_IDPEDIDO = PED_ID;
And it returns the sums and the count correctly, but I don't have a clue on how to get these seperatly per dates. So what I have is this:
VALORTOTAL | VOLUME | PESSOA |
49783.2000000 | 679780.360000| 11 |
And what I want is something like:
| DATE | VALORTOTAL | VOLUME | PESSOA |
| 2017-09-03| 1012,00 | 1209 | 12 |
| 2017-09-03| 2012,00 | 1450 | 10 |
| 2017-09-03| 3016,00 | 2500 | 20 |
| 2017-09-03| 3016,00 | 3200 | 5 |
| 2017-09-03| 2016,00 | 4000 | 9 |
You just need group by:
SELECT PED_DATA, SUM(PIT_VALORTOTAL) AS VALORTOTAL, SUM(PIT_VOLUME) AS VOLUME,
COUNT(DISTINCT PED_IDPESSOA) AS PESSOA
FROM PEDIDOS_ITENS pi INNER JOIN
PEDIDOS p
ON PIT_IDPEDIDO = PED_ID
GROUP BY PED_DATA
ORDER BY PED_DATA

Repeat all rows in left table for each unique ID in other table

I have a team of people who are scored on up to three metrics; sales, leads and Hours.
I have a table (tblScores) in MS Access which holds these scores but only if there is any. (e.g if someone had no sales there would be no entry for them for sales)
| USERID | Metric | Score |
----------------------------------
| 20511 | Sales | 12 |
| 20511 | Leads | 9 |
| 20511 | Hours | 8 |
| 20694 | Sales | 10 |
| 20694 | Hours | 7.5 |
I am trying to create an SQL query that will output three records (each possible metric) for each User in the above table including null values where they don't have an entry for that metric. e.g
| USERID | Metric | Score |
----------------------------------
| 20511 | Sales | 12 |
| 20511 | Leads | 9 |
| 20511 | Hours | 8 |
| 20694 | Sales | 10 |
| 20694 | Leads | Null |
| 20694 | Hours | 7.5 |
I have set up another table (tblMetrics) with just these 3 metrics
| Metric |
---------------
| Sales |
| Leads |
| Hours |
and tried to do a left join on the metric table against the score table
SELECT tblMetrics.*, TblScores.UserID, TblScores.Score
FROM tblMetrics LEFT JOIN TblScores ON tblMetrics.Metric = TblScores.Metric;
but it is still not giving the desired output. Does anyone know if this possible?
You need to do a CROSS JOIN first to generate all combinations, then do the LEFT JOIN to find which one are missing and assign NULL
I check access syntaxis and the CROSS JOIN should be write like this
SELECT DISTINCT M.Metric, S.USERID
FROM tblMetric M, tblScore S
And the Left Join should be
SELECT userMetrc.*, S.Score
FROM ( SELECT DISTINCT M.Metric, S.USERID
FROM tblMetric M, tblScore S
) userMetric
LEFT JOIN tblScore S
ON ( userMetric.USERID = S.USERID
AND userMetric.Metric = S.Metric )

SQL Query to Work out Every Product Combination

I require a SQL query to work out every product combination.
I have three product categories (game, accessory, upgrade) and products assigned to each of these three categories:
+----+------------+-----------+------------+
| id | category | product | prod_code |
+----+------------+-----------+------------+
| 1 | game | GTA | 100 |
| 2 | game | GTA1 | 200 |
| 3 | game | GTA2 | 300 |
| 4 | accessory | Play Pad | 400 |
| 5 | accessory | Xbox Pad | 500 |
| 6 | upgrade | Memory | 600 |
| 6 | upgrade | drive | 700 |
+----+------------+-----------+------------+
I want to take one product from each of the categories and work out every single combination:
+----+--------------+
| id | combinations |
+----+--------------+
| 1 | 100,400,600 |
| 2 | 100,500,600 |
| 3 | 100,400,700 |
| 4 | 100,500,700 |
| ? | etc |
+----+--------------+
How would I go about doing this?
Thanks in advance, Stuart
Use a CROSS JOIN:
SELECT CONCAT(t1.[prod_code], ',',
t2.[prod_code], ',',
t3.[prod_code])
FROM (
SELECT [prod_code]
FROM mytable
WHERE category = 'game') AS t1
CROSS JOIN (
SELECT [prod_code]
FROM mytable
WHERE category = 'accessory') AS t2
CROSS JOIN (
SELECT [prod_code]
FROM mytable
WHERE category = 'upgrade') AS t3
ORDER BY t1.[prod_code], t2.[prod_code], t3.[prod_code]
CROSS JOIN of derived tables, one for each category, produces the following cartesian product: 'game' products x 'accessory' products x 'upgrade' products
Demo here