Oracle sql union with no duplicate on a single column - sql

In Oracle, is it possible to perform a union where the duplicate condition is on a single column rather than the entire row?
I have table Aand B that have 2 columns: item_name, price. I'd like to create a view that for certain item_names, it looks in table A to see if the item_name is present, and if so use the price in A, if not go to B and use the price in B, then union the rest of item_name in B that have not yet been added to the view.
For example,
Table A Table B
---------------- ----------------
item_name price item_name price
---------------- ----------------
shoe 10 shoe 8
socks 2 socks 4
shirt 5 t-shirt 3
gloves 1 glasses 15
pants 7
For shoe and socks I'd like to use table A's prices if available, and if not use table B. So in the end, my view should look like this:
View
-----------------------
item_name price source
-----------------------
shoe 10 A
socks 2 A
t-shirt 3 B
glasses 15 B
pants 7 B
I tried
select * from A a
where item_name in ('shoe', 'socks')
union
select * from B b
where b.item_name not in
(select item_name from A
where item_name in ('shoe', 'socks'))
Which I don't like because the query select * from A where item_name in ('shoe', 'socks') is duplicated. Is there a better/more efficient way of doing this?

I think you are looking for a join:
select coalesce(a.item_name, b.item_name) as item_name,
coalesce(a.price, b.price) as price,
(case when a.price is not null then 'A' else 'B' end) as source
from a full outer join
b
on a.item_name = b.item_name

Since you are using Oracle, I may suggest the following, it would do the trick
select NVL(A.ITEM_NAME,B.ITEM_NAME) AS ITEM_NAME,
NVL(A.PRICE,B.PRICE) AS PRICE
FROM A as a RIGHT JOIN B as b ON A.ITEM_NAME=B.ITEM_NAME
To understand why it works, simply try it without NVL, the resulting right join results
A_item A_price B_item B_price
shoe 10 shoe 8
socks 2 socks 4
(null) (null) glasses 15
(null) (null) t-shirt 3
(null) (null) pants 7
Since you do not want the null values from table A, use NVL
NVL has also equivalent functions in mysql/mssql etc

Try this,
create view viewname as (
select coalesce(a.item_name, b.item_name) as item_name,
coalesce(a.price, b.price) as price,
(case when a.item_name=b.item_name then 'A' else 'B' end) as source
from tablea a right outer join
tableb b
on a.item_name = b.item_name)
made slight change Gordon's ans

Related

SQL join with composite key

I'm struggling with a SQL join query, maybe you could help me.
I have to join two tables. In one table, the primary key is a composite key from two columns to uniquely define a row.
In the other table, I just have one of these two rows.
Table looks like this:
Table 1
Type (PK)
Code (PK)
Description
f.e.
Type
Code
Description
0
A
potential A
0
A+
potential A+
1
1
Premium
1
2
Mainstream
2
EFI
EFI
2
LK
Local Key
3
100
exclusive
3
105
inclusive
In the second table, I have the code as the foreign key, but multiple times:
Table 2
Number (PK)
Name
Address
Code 1 (FK)
Code 2 (FK)
Code 3 (FK)
I want a query, where it shows me, f.e the customer name, code 1, code 2 and code 3 and the corresponding description.
If I have a query like this:
SELECT a.Number, a.Name, a.Code1, b.description, a.Code2, b.description
FROM Table1 a
INNER JOIN Table2 b ON a.Code1 = b.Code
it always shows me the first description. So, if Code 1 = A -> Description: potential A, if Code 2 = 1 -> Description = potential A - even if the correct description is "Premium".
I hope it's clear what I'm trying to do.
Thanks for your help!
Table 1 sample data:
Number (PK)
Name
Address
Code1
Code2
Code 3
Code 4
1587452
BMW
Downstreet5
A
2
LK
105
1887525
Audi
Abbyroad 7
A+
1
EFI
100
Table 2 sample data see above
Expected output:
New Query / View with
Name
Code1
Description Code 1
Code2
Description Code 2
BMW
A
potential A
2
Mainstream
What I get:
SELECT a.Number, a.Name, a.Code1, b.description, a.Code2, b.description
FROM Table1 a
INNER JOIN Table2 b ON a.Code1 = b.Code
| BMW | A | potential A | 2 | potential A | LK | potential A | 105 | potential A |
Solution if someone else does have the same problem:
I solved it with a subquery, like:
SELECT a.Number, a.Name, a.Code1,Description =
(SELECT b.description
FROM Table2 b
WHERE a.Code1 = b.Code),
a.Code2, Description =
(SELECT b.description
FROM Table2 b
WHERE a.Code2 = b.Code),
FROM Table1 a
Thanks for your help!

Join values in one table only when the minimum value is less than value in other table - snowflake

I have two tables:
Table A
Purchase_date
Product_ID
20200101
1
20190101
2
20200301
1
20201201
2
Table B
Product_ID
Price
Price_change_date
1
10
20191231
2
15
20201031
1
12
20200110
1
20
20201231
2
8
20190331
I want to join these two tables based on two criteria:
If the purchase_date < min(price_change_date), return the price corresponding to the min(price_change_date)
Else return the price at the max(price_change_date) that is less than the purchase_date
I have written a query to successfully get results for the second criteria, but not the first, and I'm not sure if they can be combined within the same query.
Results for the above table should yield:
Results
Purchase_date
Product_ID
Price
Price_change_date
20200101
1
10
20191231
20190101
2
8
20190331
20200301
1
12
20200110
20201201
2
15
20201031
Notice the second row is the one that returns a price with a purchase date that precedes the price_change_date.
Thanks in advance!!
You can use a lateral join:
select a.*, b.*
from a, lateral
(select b.*
from b
where b.product_id = a.product_id and b.price_change_date <= a.purchase_date
order by b.price_change_date desc
limit 1
) b;
EDIT:
The above gives you the most recent price change information. If you want records in a before the original price, then you can use:
select a.*
from a left join
(select b.product_id, min(b.price_change_date) as min_price_change_date
from b
group by product_id
) b
on a.purchase_date < b.price_change_date;

How to use "with" in sql?

I am having a problem with this, i have this code
with
a as(select * from CASH_DENOM where Curr_SourceId=1 and convert(date,Curr_EntryDate)='2015-1-28'),
b as (select * from CASH_DENOM where Curr_SourceId=2 and convert(date,Curr_EntryDate)='2015-1-28')
select * from a, b where a.Curr_EntryDate=b.Curr_EntryDate
table a has a result of of 7 rows and table b has also 7 rows but when i run this code it returns me 49 rows why is it like that? it is supposed to be only 14 rows..
EDITS/UPDATE:
with
a as(select Curr_EntryDate date1, Curr_Denom denom, Curr_Pieces pcs, Curr_Total ctotal from CASH_DENOM where Curr_SourceId=1 and convert(date,Curr_EntryDate)='2015-1-28'),
b as (select Curr_EntryDate date2, Curr_Denom denom1, Curr_Pieces pcs1, Curr_Total ctotal1 from CASH_DENOM where Curr_SourceId=2 and convert(date,Curr_EntryDate)='2015-1-28')
select a.denom,a.pcs,a.ctotal,b.denom1,b.pcs1,b.ctotal1 from a inner join b on a.date1=b.date2;
by the way i am doing inner join because i want to have 6 columns (denom, pcs,ctotal,denom1, pcs1,ctotal1) and 14 rows. is this possible?.
Using union gives me only 4 columns (date2, denom1, pcs1, ctotal1) and 14 rows
UPDATE II
The output should be like this:
-------------------------------------------------------
denom | pcs | ctotal | denom1 | pcs1 | ctotal1 |
-------------------------------------------------------
100.00 | 2 | 200.00 | 100.00 | 5 | 500.00 |
-------------------------------------------------------
heres the picture
Something like that..
Thanks in advance..
Well, its either you want to join those results together, and then you should have 7 results and not 14, or in your case 49 because your join condition equals to 1=1(you are comparing date fields that are always 28/1/15 on all of them) so each row is beeing attached to each row. In that case you should change your join condition from
where a.Curr_EntryDate=b.Curr_EntryDate --(Equals to 1=1)
to something like:
where a.ID_FIELD=b.ID_FIELD --(fields that have the same value that you want to combine their results)
Or, what you want is just to unite those two results, and in that case you are looking for a union and you need this:
with
a as(select * from CASH_DENOM where Curr_SourceId=1 and convert(date,Curr_EntryDate)='2015-1-28'),
b as (select * from CASH_DENOM where Curr_SourceId=2 and convert(date,Curr_EntryDate)='2015-1-28')
select * from a
union all
select * from b
And lastly, you don't even need two selects / union / join / with or anything, this can be done with one select and IN statement like this:
select * from CASH_DENOM
where Curr_SourceId in (1,2)
and convert(date,Curr_EntryDate)='2015-1-28'
You need a union. You are doing a join. Think about the condition "where a.Curr_EntryDate=b.Curr_EntryDate"
7 rows from a have that value and 7 rows from b have that value, making 7*7 = 49 rows
Try
Select a union all select b

SQL Join w/ some Math thrown in

Heck, maybe 'joining' isn't even involved. I'm way out of my sql league here. Could someone please help me out w/ the following:
Table A
ItemId ItemLookup Price
------- ---------- -----
1 123456 10.00
2 234567 7.00
3 345678 6.00
Table B
ItemId Location Qty QtyOnHold
------- ---------- ----- ---------
1 1 26 20
2 1 0 0
3 1 12 6
1 2 4 0
2 2 2 1
3 2 16 8
What I'm hoping to get is something that looks like
ItemLookup, Price, (qty minus qtyonhold for loc1), (qty minus qtyonhold for loc2)
or 123456, 10.00, 6, 4
Thank you very much for any direction you can provide.
You can use conditional aggregation and a join:
select a.ItemLookup,
sum(case when Location = 1 then Qty - QtyOnHold end) as Location1,
sum(case when Location = 2 then Qty - QtyOnHold end) as Location2
from tableb b join
tablea a
on b.ItemId = a.ItemId
group by a.ItemLookup;
Somthing like this
select tablea.* ,
(select (qty- QtyOnHold) as qty from tableb where ItemId = tablea.ItemId ans Location = 1 ) as qtyl1,
(select (qty- QtyOnHold) as qty from tableb where ItemId = tablea.ItemId ans Location = 2) as qtyl2
from tablea
This assumes that there's only one row in TableB for each ItemID + Location combination. This is basically just a "pivot", you can learn various ways to do this in MySQL here.
SELECT ItemLookup, Price,
MAX(IF(Location = 1, Qty-QtyOnHold, 0)) avail1,
MAX(IF(Location = 2, Qty-QtyOnHold, 0)) avail2
FROM TableA AS a
JOIN TableB AS b ON a.ItemId = b.ItemId
GROUP BY a.ItemId
It seems to me that it may be possible to have a variable number of locations for each item. If this is the case, you need an aggregate function to convert/concatenate multiple rows into a column.
Here's an example with MySQL's group_concat function:
select a.itemlookup,a.price,group_concat('loc ',location,'=',b.x order by location) as qty_minus_qtyonhold
from tablea a,(select itemid,location,qty-qty_onhold x from tableb
group by itemid,location) as b
where a.itemid = b.itemid
group by 1
You'll get a result like this:
itemlookup price qty_minus_qtyonhold
---------- ------ ------------------
123456 10.00 loc 1=6,loc 2=4
234567 7.00 loc 1=0,loc 2=1
345678 6.00 loc 1=6,loc 2=8
Not sure what DBMS you're using but there are similar alternatives for Oracle and SQL Server

Join with table

Say I have two tables as below where Table A has columns name and type where each name may appear many times and with different type and Table B has unique code, name and sum.
Table A
John Type1
Mark Type2
John Type1
Mark Type3
John Type4
Paul Type5
Table B
1 John 20
2 Mark 33
3 Paul 22
4 Mark 55
5 John 46
Now what I want is something like this:
Table C
1 John 20 Type1
2 Mark 33 Type2
3 Paul 22 Type5
4 Mark 55 Type2
5 John 46 Type1
Normally Table A should contain unique entries with one type for each name and I could do a right join Table B on name to get what I want. But now if I do right join I get duplicate entries on Table C because name has duplicates types in Table A. How do I solve this?
Try this
WITH TableAA
AS
(
SELECT ROW_NUMBER() OVER(PARTITION BY NAME ORDER BY NAME,TYPE) RN
,name
,type
FROM TableA
)
SELECT
B.*
,(
SELECT type from TableAA A WHERE A.name= B.name AND A.RN=
ISNULL(NULLIF((SELECT COUNT(1) FROM TableB C WHERE C.NAME=B.name
and C.no < B.no),0),1)
) AS Type
FROM
TableB B
SQLFiddle Demo
You can try this :
SELECT A.*,B.TYPE
FROM dbo.TABLE_2 A RIGHT JOIN
(
SELECT DISTINCT(NAME),MIN(TYPE)TYPE
FROM TABLE_1
GROUP BY NAME
) B ON A.NAME=B.NAME
ORDER BY CODE ASC
TABLE_1= TABLE A
TABLE_2= TABLE B