SQL Join w/ some Math thrown in - sql

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

Related

SQL query 'Join'

TABLE A >>
ItemNo Name
1 Item1
2 Item2
3 Item3
4 Item4
TABLE B >>
ItemNo Status
1 available
1 onhold
1 Sold
1 Transit
------------------
2 available
2 onhold
2 Sold
-----------------
3 Transit
-----------------
4 onhold
There are four status on table B onhold,available,sold,Transit
Expected result: if anyone of the itemNo in table B has on either available or Transit,
then I need to get only the table A record of that ItemNo
Output:
Table A:
ItemNo Name
1 Item1
2 Item2
3 Item3
Please advise on how to write MS SQL query for this. Many thanks in advance!
You can solve with a correlated subquery:
SELECT *
FROM tableA ta
WHERE tA.ItemNo IN
(
SELECT tB.ItemNo
FROM tableB tB
WHERE tB.Status in ('available','Transit')
AND tB.ItemNo = ta.ItemNo
)
Join the tables and use SELECT DISTINCT to get just one row from table A.
SELECT DISTINCT a.*
FROM TableA AS a
JOIN TableB AS b ON a.itemNo = b.itemNo
WHERE b.status in ('available', 'Transit')

SQL. How to select multiple rows by using the MIN and GROUP BY

ID UserId Name Amount RewardId
----------------------------
1 1 James 10.00 1
2 1 James 10.00 2
3 1 James 10.00 3
4 2 Dave 20.00 1
5 2 Dave 20.00 3
6 3 Lim 15.00 2
I'm trying to insert to another table, and this is the result that i'm struggling with:
Tbl1ID RewardId
------------------
1 1
1 2
1 3
4 1
4 3
6 2
I'm trying to get the MIN(ID) of each person and select all the RewardId that belong to that person.
You could do a simple self join to get the minimum id value per userid/rewardid combination;
SELECT MIN(a.id) Tbl1ID, b.RewardId
FROM mytable a
JOIN mytable b
ON a.name = b.name
GROUP BY b.userid, b.rewardid
ORDER BY tbl1id, rewardid;
An SQLfiddle to test with.
If you are running SQL Server 2008+, you can simplify it by using Window Function.
INSERT INTO AnotherTable (Tbl1ID, RewardID)
SELECT MIN(ID) OVER (PARTITION BY Name),
RewardID
FROM SourceTable
SQLFiddle Demo
Try this
SELECT tbl1id,RewardID From
table1 S JOIN
(
SELECT MIN(ID) as tbl1id,Name FROM table1 GROUP BY Name
) T ON T.Name = S.Name
ORDER BY tbl1id
FIDDLE DEMO
Output:
Tbl1ID RewardId
----------------
1 1
1 2
1 3
4 1
4 3
6 2
If you want insert into new table then try this out
Insert into Newtable (tbl1id,RewardID)
SELECT tbl1id,RewardID from
table1 S JOIN
(
SELECT MIN(ID) as tbl1id,Name
FROM table1
GROUP BY Name
) T ON T.Name = S.Name
ORDER BY tbl1id;
FIDDLE DEMO

Select Distinct value from column and return all rows

I'm trying to select distinct value from a column but return all rows related to the values selected. In psuedo code it will look like this.
SELECT *
FROM table
WHERE field is Distinct
I googled the question and I've tried using GROUP BY but the query never executes. Thanks for the help.
I am using a Microsoft SQL Database.
The Data looks like this:
CodeId Code CatalogType CodeGroup CodeText CodeGroupText CodeDesc State_ID
------- ----- ------------- ---------- -------- -------------- --------- ---------
1 AAAA 1 100 Plastic Plastic Center NULL 2
2 BBBB 1 100 Glass Glass Center NULL 2
3 CCCC 1 101 Steel Steel Center NULL 2
I just want to the data to look the same just where the code group is distinct.
Data would look like this:
CodeId Code CatalogType CodeGroup CodeText CodeGroupText CodeDesc State_ID
------- ----- ------------- ---------- -------- -------------- --------- ---------
1 AAAA 1 100 Plastic Plastic Center NULL 2
3 CCCC 1 101 Steel Steel Center NULL 2
You could always use a subquery to return the min(codeid) for each codegroup and join this result to your table:
select t1.codeid,
t1.code,
t1.catalogtype,
t1.codegroup,
t1.codetext,
t1.codegrouptext,
t1.codedesc,
t1.state_id
from yourtable t1
inner join
(
select MIN(codeid) codeid, codegroup
from yourtable
group by codegroup
) t2
on t1.codeid = t2.codeid
and t1.codegroup = t2.codegroup
In most databases, you can do:
select t.*
from (select t.*
row_number() over (partition by field order by field) as seqnum
from t
) t
where seqnum = 1
SELECT field1,field2,max(field3),sum(field4)
FROM table
GROUP BY field1, field2
This will give you all distinct field1 and field2's. You cannot get the field3 field directly (with this grouping), since there may be multiple field3's.
if you need just distinct values from a column, i.e. to find a set of unique values in one column, then this code helps you (for sql server at least):
select distinct
columnName
from tableT

Oracle sql union with no duplicate on a single column

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

Group by + joins

Hi I am having a problems using Group By and joins between 3 tables.
I have a project table with various fields and a projectcode fields. I then have an invoice table and an hours table and each can have multiple rows per project. Both of these table have project code also.
The two SUM values are not calculating correctly and I am realy struggling to see where the issue is.
Here the sql I am using:
SELECT dbo.project.projectcode,
dbo.project.client,
dbo.project.project,
dbo.project.budget,
dbo.project.budget * 80 AS value,
SUM(dbo.harvest.hours) AS hourslogged,
SUM(dbo.salesforce.value) AS invoiced
FROM dbo.salesforce
RIGHT OUTER JOIN dbo.project
ON dbo.salesforce.projectcode = dbo.project.projectcode
LEFT OUTER JOIN dbo.harvest
ON dbo.project.projectcode = dbo.harvest.projectcode
GROUP BY dbo.project.projectcode,
dbo.salesforce.projectcode,
dbo.harvest.projectcode,
dbo.project.project,
dbo.project.client,
dbo.project.budget
Any help or tips on this would be much appreciated!
Whenever each of the two tables, dbo.salesforce and dbo.harvest, have more than 1 match for every projectcode, a mini-Cartesian product happens. Here's a simple illustration. Suppose there are tables A and B, like this:
Table A:
AID AVALUE
--- -------
1 ValueA1
2 ValueA2
Table B:
BID BVALUE AID
--- ------- ---
1 ValueB1 1
2 ValueB2 1
3 ValueB3 2
Now if we performed this join:
SELECT * FROM A JOIN B ON A.AID = B.AID
the result would be:
AID AVALUE BID BVALUE AID
--- ------- --- ------- ---
1 ValueA1 1 ValueB1 1
1 ValueA1 2 ValueB2 1
2 ValueA2 3 ValueB3 2
Enter table C:
CID CVALUE AID
--- ------- ---
1 ValueC1 1
2 ValueC2 1
3 ValueC3 1
And the join now is this:
SELECT * FROM A JOIN B ON A.AID = B.AID JOIN C ON A.AID = C.AID
What would be the result? Here:
AID AVALUE BID BVALUE AID CID CVALUE AID
--- ------- --- ------- --- --- ------- ---
1 ValueA1 1 ValueB1 1 1 ValueC1 1
1 ValueA1 1 ValueB1 1 2 ValueC2 1
1 ValueA1 1 ValueB1 1 3 ValueC3 1
1 ValueA1 2 ValueB2 1 1 ValueC3 1
1 ValueA1 2 ValueB2 1 2 ValueC3 1
1 ValueA1 2 ValueB2 1 3 ValueC3 1
As you can see, every match from B is repeated three times, for how many matches C has got. And, similarly, every match from C is repeated twice, because that is how many matches there are in B. The 'luckiest', of course, is the row from A, because it is repeated 2 × 3 = 6 times. That is a Cartesian join for you. And that's just what happens in your case too.
Not sure whether it is considered typical, but in such cases I would often group each table separately by the joining expression(s), then join the result sets. Your query would then look like this:
SELECT
p.projectcode,
p.client,
p.project,
p.budget,
p.budget * 80 AS value,
h.hourslogged,
s.invoiced
FROM dbo.project p
LEFT JOIN (
SELECT
projectcode,
SUM(dbo.salesforce.value) AS invoiced
FROM dbo.salesforce
GROUP BY projectcode
) s ON p.projectcode = s.projectcode
LEFT JOIN (
SELECT
projectcode,
SUM(dbo.harvest.hours) AS hourslogged
FROM dbo.harvest
GROUP BY projectcode
) h ON p.projectcode = h.projectcode
I'd suggest to avoid mixing right and left outer join.
Your central table is Project, so use it first.
SELECT dbo.project.projectcode,
dbo.project.client,
dbo.project.project,
dbo.project.budget,
dbo.project.budget * 80 AS value,
SUM(dbo.harvest.hours) AS hourslogged,
SUM(dbo.salesforce.value) AS invoiced
FROM dbo.project
LEFT OUTER JOIN dbo.salesforce
ON dbo.salesforce.projectcode = dbo.project.projectcode
LEFT OUTER JOIN dbo.harvest
ON dbo.project.projectcode = dbo.harvest.projectcode
GROUP BY dbo.project.projectcode,
dbo.project.project,
dbo.project.client,
dbo.project.budget
But the error come from the GROUP BY. You don't have to group by the two tables on which you are doing the aggregate, else your aggregate will not be good !