How to conditionally join two tables - sql

I have two tables which I need to join depending upon their values.
TABLE coursemat
+-----+--------+----------+
| txt | price | material |
+-----+--------+----------+
Table coprices
+--------+----------+
| price | material |
+--------+----------+
They are connected by the material key.
If I search coursemat.material and find that coprices.material is equal, then I must use coprices.price instead of the coursemat.price.
This is what I have so far:
SELECT coursemat.txt, coursemat.price, coursemat.material, coprices, country
FROM coursemat
JOIN corprices ON coursemat.material = coprices.material;
But this isn't quite getting what I want.
Essentially, I want to use coursemat.price if coprices.price does not exist for the same material and coprices.material does exist, then I want to use coprices.price instead of coursemat.price.

If I understand what you want correctly you can use a left join and the IFNULL statement:
SELECT
coursemat.txt,
IFNULL(coprices.price, coursemat.price),
coursemat.material,
coprices.country
FROM
coursemat
LEFT JOIN
corprices
ON coursemat.material = coprices.material;

Another option is COALESCE:
SELECT cm.txt, cm.material, COALESCE(cp.price, cm.price) AS price
FROM coursemat cm
LEFT JOIN corprices cp ON cm.material = cp.material;
Finally, you could also use a CASE statement:
SELECT cm.txt, cm.material,
CASE WHEN cp.price IS NOT NULL THEN cp.price ELSE cm.price END AS price
FROM coursemat cm
LEFT JOIN corprices cp ON cm.material = cp.material;

Related

SQL join two tables by modifying on columns

I have 3 tables on PostgreSQL:
SPENDINGS:
store |spending
-----------+---------
0000700551 | $75
STORE:
store | zip_code
-------+---------
700551 | XXP PDD
CUSTOMER:
id | zip_code
----+----------
002 | XXPPDD
I would love to join these tables like this:
right(SPENDIGNS.store, 6) = STORE.store and
trim(STORE.zip_code) = CUSTOMER.zip_code.
How can I do that could you help me out please?
trim(store.zip_code) is not good for the job because the whitespace is within the zip code text. Use replace(store.zip_code, ' ', '') instead. right(spendings.store, 6) does not seem safe to me too. Better use ltrim(spendings.store, '0') to remove leading zeroes. SQL Fiddle
select *
from spendings sp
join store st on ltrim(sp.store, '0') = st.store
join customer cu on cu.zip_code = replace(st.zip_code, ' ', '');
BTW your data design does need improvement.
try something as follows (you can use what ever join you would like to in place of inner)
select spendings.spending, store.store,customer.zipcode
from spendings inner join store on right(SPENDIGNS.store, 6) = STORE.store
inner join customer on trim(STORE.zip_code) = CUSTOMER.zip_code

Select highest value based off of a different column

I am trying to get the highest value based off of another column.
SELECT DISTINCT
AppDetailVehicleValuation.AppID,
VehicleValuationOption.Description,
MAX (VehicleValuationOptionValueType.Value)
FROM
AppDetailVehicleValuation
INNER JOIN VehicleValuationOption
ON AppDetailVehicleValuation.ValuationID = VehicleValuationOption.ValuationID
INNER JOIN VehicleValuationOptionValueType
ON VehicleValuationOption.ValuationOptionID = VehicleValuationOptionValueType.ValuationOptionID
WHERE
(VehicleValuationOption.IsSelected LIKE '1')
AND (VehicleValuationOption.IsSystemOption LIKE '1')
What I have is this
AppID | Description | Value
999 Beats Audio 425.00
999 Beats Audio 475.00
999 Power Str. 600.00
999 Power Str. 750.00
this is what I need
AppID | Description | Value
999 Beats Audio 475.00
999 Power Str. | 750.00
You are just missing a GROUP BY clause in your query:
SELECT
AppDetailVehicleValuation.AppID,
VehicleValuationOption.Description,
MAX (VehicleValuationOptionValueType.Value)
FROM
AppDetailVehicleValuation
INNER JOIN VehicleValuationOption
ON AppDetailVehicleValuation.ValuationID = VehicleValuationOption.ValuationID
INNER JOIN VehicleValuationOptionValueType
ON VehicleValuationOption.ValuationOptionID = VehicleValuationOptionValueType.ValuationOptionID
WHERE
(VehicleValuationOption.IsSelected LIKE '1')
AND (VehicleValuationOption.IsSystemOption LIKE '1')
GROUP BY AppDetailVehicleValuation.AppID, VehicleValuationOption.Description
You can simply do this:
SELECT
t.AppId,
t.Description,
max(t.Value)
FROM mytable t
GROUP BY t.description, t.AppId
This is too long for a comment.
Glad you found an answer that works with the GROUP BY. I would suggest you start using aliases in your queries. It can quickly and easily turn a wall of text into something fairly easy to see what is going on. You might end with something along these lines.
SELECT advv.AppID
, vvo.Description
, MaxValue = MAX(vvot.Value)
FROM AppDetailVehicleValuation advv
INNER JOIN VehicleValuationOption vvo ON advv.ValuationID = vvo.ValuationID
INNER JOIN VehicleValuationOptionValueType vvot ON vvo.ValuationOptionID = vvot.ValuationOptionID
WHERE vvo.IsSelected = '1'
AND vvo.IsSystemOption = '1'
group by advv.AppID
, vvo.Description

Where clause with multiple values

I am new to SQL and I am using MS SQL Server Management Studio 2014. I have 3 tables called Pizza, Pizza_Topping and Topping. I want to list the pizzas that have TOPPING1 and TOPPING2 as toppings.
This is what I have came up with,
select Pizza.pizzaID, Pizza.pizzaName, Topping.toppingName
from Pizza left join Pizza_Topping on
Pizza.pizzaID = Pizza_Topping.pizzaID
left join Topping on
Topping.toppingID = Pizza_Topping.toppingID
where Topping.toppingName in ('topping1', 'topping2')
and this gives me
pizzaID pizzaName toppingName
-------- ---------------- --------------
PZ002 | PIZZA1 | TOPPING1
PZ002 | PIZZA1 | TOPPING2
PZ010 | PIZZA5 | TOPPING1
PZ010 | PIZZA5 | TOPPING2
PZ011 | PIZZA6 | TOPPING1
PZ012 | PIZZA7 | TOPPING2
I only need first four rows because last two pizzas only have one of the toppings not both.
I have tried this as well,
GROUP BY Pizza.pizzaID,Pizza.pizzaName, Topping.toppingName HAVING COUNT(toppingName) >= 2
but it didn't give the expected result. that line can be used if I am going to display only pizzaID and pizzaName but i want to display toppingName as well.
to be more clear, this what I am expecting
pizzaID pizzaName toppingName
-------- ---------------- --------------
PZ002 | PIZZA1 | TOPPING1
PZ002 | PIZZA1 | TOPPING2
PZ010 | PIZZA5 | TOPPING1
PZ010 | PIZZA5 | TOPPING2
Please tell me how to achieve this result. Thanks
You are almost correct. You need to remove ToppingName from the GROUP BY:
GROUP BY Pizza.pizzaID, Pizza.pizzaName
HAVING COUNT(toppingName) >= 2
You also need to remove it from the SELECT if it is also there:
select p.pizzaID, p.pizzaName
from Pizza p join
Pizza_Topping pt
on p.pizzaID = pt.pizzaID join
Topping t
ont.toppingID = pt.toppingID
where t.toppingName in ('topping1', 'topping2')
group by p.pizzaID, p.pizzaName
having count(*) >= 2;
Also notice two things. Because you have a condition on toppings, the left join is unnecessary. You are only looking for matches, so inner join is appropriate. The use of table aliases makes the query easier to write and to read.
This assumes that pizzas cannot have duplicate toppings. If so, then change the last condition to:
having count(distinct t.toppingName) >= 2
I think this could be done more easily if you can get the count() in your subquery and then join back all the pizzaid's that have count greater than 1 something like this:
SELECT q1.pizzaID
,q1.PizzaName
,t.ToppingName
FROM (
SELECT Pizza.pizzaID
,Pizza.pizzaName
,count(topping.ToppingName) total_count
FROM Pizza
INNER JOIN Pizza_Topping ON Pizza.pizzaID = Pizza_Topping.pizzaID
INNER JOIN Topping ON Topping.toppingID = Pizza_Topping.toppingID
GROUP BY Pizza.pizzaID
,Pizza.pizzaName
HAVING count(topping.ToppingName) > 1
) q1
INNER JOIN Pizza_Topping pt ON q1.pizzaID = pt.pizzaID
INNER JOIN Topping t ON t.toppingID = pt.toppingID
WHERE t.toppingName in ('topping1', 'topping2')
Get your result without topping name . Wrap it in a view and do an inner join with Topping table .
To include the toppings you can do the COUNT-logic using Windowed Aggregate Functions:
SELECT p.pizzaID, p.pizzaName, dt.toppingName
FROM Pizza AS p
JOIN
(
SELECT p.pizzaID, t.toppingName,
COUNT(*) OVER (PARTITION BY p.pizzaID) AS cnt -- number of matching toppings
FROM Pizza_Topping AS pt
JOIN Topping AS t
ON t.toppingID = pt.toppingID
WHERE t.toppingName IN ('topping1', 'topping2')
) AS dt
ON p.pizzaID = dt.pizzaID
WHERE cnt = 2 -- number of searched toppings
You have to join to the pizza_topping and topping tables twice.
select yourfields
from pizza join pizza_topping pt1 on pizza.pizzaID = pt1.pizzaID
join topping t1 on pt1.toppingID = t1.toppingId
join pizza_topping pt2 on pizza.pizzaID = pt2.pizzaID
join topping t2 on pt2.toppingID = t2.toppingId
where t1.toppingName = 'topping1'
and t2.toppingName = 'topping2'
You can solve this problem by using group by statement restricted to Pizza Id. Please refer below query.
select Pizza.pizzaID, Pizza.pizzaName, Pizza_Toppings.Remark
from Pizza join Pizza_Toppings on
Pizza.pizzaID = Pizza_Toppings.pizzaID
join Toppings on
Toppings.toppingID = Pizza_Toppings.toppingID
where Pizza.PizzaId in (
select Pizza.PizzaID
from Pizza join Pizza_Toppings on
Pizza.pizzaID = Pizza_Toppings.pizzaID
join Toppings on
Toppings.toppingID = Pizza_Toppings.toppingID
where Toppings.TopingName in ('topping1', 'topping2')
group by pizza.PizzaId
having count(Pizza.PizzaId ) > 1)
Please let me know if this does not work for you.

Show column value of one of left joins

In this Query shows one row if SAL.ID_AMENAZA exists in MFT or MFA schemas, but i want to show MFT.ID_AMENAZA or MFA.ID_AMENAZA (if exist in any table) in the resulting row.
In my actual query i dont require the ID_AMENAZA. How can i show?
SELECT SAL.ID_SALVAGUARDA, SAL.DESCRIPCION, SAL.EFICACIA
FROM AGR_SALVAGUARDAS SAL
LEFT JOIN AGR_MIT_FREC_TIPO MFT
ON SAL.ID_SALVAGUARDA = MFT.ID_SALVAGUARDA AND MFT.ID_AMENAZA = 5043
LEFT JOIN AGR_MIT_FREC_ACT MFA
ON SAL.ID_SALVAGUARDA = MFA.ID_SALVAGUARDA AND MFA.ID_AMENAZA = 5043
WHERE MFT.ID_SALVAGUARDA IS NOT NULL OR
MFA.ID_SALVAGUARDA IS NOT NULL
GROUP BY SAL.ID_SALVAGUARDA, SAL.DESCRIPCION, SAL.EFICACIA
I need to obtain a table like this:
ID_SALVAGUARDA | DESCRIPCION | EFICACIA | ID_AMENAZA
5061 | PRE-01 | 100 | 5043
Thank you in advance.
This should work for you:
SELECT SAL.ID_SALVAGUARDA, SAL.DESCRIPCION, SAL.EFICACIA, 5043 as ID_AMENAZA
FROM AGR_SALVAGUARDAS SAL
LEFT JOIN AGR_MIT_FREC_TIPO MFT
ON SAL.ID_SALVAGUARDA = MFT.ID_SALVAGUARDA AND MFT.ID_AMENAZA = 5043
LEFT JOIN AGR_MIT_FREC_ACT MFA
ON SAL.ID_SALVAGUARDA = MFA.ID_SALVAGUARDA AND MFA.ID_AMENAZA = 5043
WHERE MFT.ID_SALVAGUARDA IS NOT NULL OR
MFA.ID_SALVAGUARDA IS NOT NULL
GROUP BY SAL.ID_SALVAGUARDA, SAL.DESCRIPCION, SAL.EFICACIA;
because your query guarantees that there is at least one match.
However, the more general solution is:
SELECT SAL.ID_SALVAGUARDA, SAL.DESCRIPCION, SAL.EFICACIA,
cOALESCE(MFT.ID_AMENAZA, MFA.ID_AMENAZA) as ID_AMENAZA
. . .
The function COALESCE() returns the first value that is not NULL in its list of arguments.
You can coalesce for this.
SELECT
SAL.ID_SALVAGUARDA,
SAL.DESCRIPCION,
SAL.EFICACIA,
COALESCE( MFT.ID_AMENAZA, MFA.ID_AMENAZA) as ID_AMENAZA
This will return the first ID that is not null.

SQL Server : update a table with values from same table based on IDs in a second table

I have a table called STOCK that I want to update that looks like this:
-------------------------
NUMBER | UNITS
-------------------------
NA13ALPK1010 | 9
NA13ALANA1010 | 11
NA13ALPK1065 | 4
NA13ALANA106 | 5
ON0003 XS | 1
AT3322 2 | 3
Based on a second table called PKGINV that looks like this:
----------------------------------------
PKGNUMBER | BOARDNUMBER
----------------------------------------
NA13ALPK1010 | NA13ALANA1010
NA13ALPK1065 | NA13ALANA106
First, I'd like to be able to do a SELECT with some type of join so that I can show UNITS from STOCK for both PKGNUMBER and BOARDNUMBER in PKGINV.
Then, I'd like to update UNITS in STOCK when PKGINV.PKGNUMBER = STOCK.NUMBER with the PKGINV.BOARDNUMBER UNITS from STOCK.
So after the update, NA13ALPK1010 would have UNITS of 11 (the UNITS from NA13ALANA1010) and NA13ALPK1065 would have UNITS of 5 (the UNITS from NA13ALANA106).
Thanks in advance for your help on this!
Your SELECT is simply a matter of joining the STOCK table twice:
SELECT
PKGNUMBER_Stock.UNITS AS PKGNUMBER_UNITS
,BOARDNUMBER_Stock.UNITS AS BOARDNUMBER_UNITS
FROM
PKGINV AS pkginv
JOIN STOCK AS PKGNUMBER_Stock ON PKGNUMBER_Stock.NUMBER = pkginv.PKGNUMBER
JOIN STOCK AS BOARDNUMBER_Stock ON BOARDNUMBER_Stock.NUMBER = pkginv.BOARDNUMBER
Likewise, the UPDATE statement should simply match the join set above:
UPDATE PKGNUMBER_Stock
SET PKGNUMBER_Stock.UNITS = BOARDNUMBER_Stock.UNITS
FROM
PKGINV AS pkginv
JOIN STOCK AS PKGNUMBER_Stock ON PKGNUMBER_Stock.NUMBER = pkginv.PKGNUMBER
JOIN STOCK AS BOARDNUMBER_Stock ON BOARDNUMBER_Stock.NUMBER = pkginv.BOARDNUMBER
You need to join Stock to PKGINV two times:
Update s
set s.UNITS = s2.UNITS
from STOCK s
join PKGINV p on s.NUMBER = p.PKGNUMBER
join STOCK s2 on p.BOARDNUMBER = s2.NUMBER