LEFT JOIN in 2 table - sql

Why doesn't this query execute correctly?
SELECT pr.ProjectNumber,
SUM(ma.TotalAmount) As CostOfMaterials,
SUM(ot.TotalAmount) AS CostOfOthers
FROM [dbo].[tblProject] AS pr
LEFT JOIN [dbo].[tblCostOfMaterials] AS ma ON pr.ProjectNumber=ma.ProjectNumber
LEFT JOIN [dbo].[tblCostOfOthers] AS ot ON pr.ProjectNumber=ot.ProjectNumber
GROUP BY pr.ProjectNumber

I assume that you mean that because each table has multiple rows for a particular project number you count the same values multiple times. To avoid this you can use
;WITH ma
AS (select ProjectNumber,
SUM(TotalAmount) as CostOfMaterials
FROM [dbo].[tblCostOfMaterials]
GROUP BY ProjectNumber),
ot
AS (select ProjectNumber,
SUM(TotalAmount) as CostOfOthers
FROM [dbo].[tblCostOfOthers]
GROUP BY ProjectNumber)
SELECT pr.ProjectNumber,
CostOfMaterials,
CostOfOthers
FROM [dbo].[tblProject] AS pr
LEFT JOIN ma
ON pr.ProjectNumber = ma.ProjectNumber
LEFT JOIN ot
ON pr.ProjectNumber = ot.ProjectNumber
The reason why you get that behaviour is because you are getting mini Cartesian joins
WITH tblProject (ProjectNumber) AS
(
SELECT 1
),tblCostOfMaterials(ProjectNumber, TotalAmount) AS
(
SELECT 1,101 UNION ALL
SELECT 1,201 UNION ALL
SELECT 1,301
),
tblCostOfOthers(ProjectNumber, TotalAmount) AS
(
SELECT 1,100 UNION ALL
SELECT 1,200
)
SELECT *
FROM [tblProject] AS pr
LEFT JOIN [tblCostOfMaterials] AS ma ON pr.ProjectNumber=ma.ProjectNumber
LEFT JOIN [tblCostOfOthers] AS ot ON pr.ProjectNumber=ot.ProjectNumber
Returns
ProjectNumber ProjectNumber TotalAmount ProjectNumber TotalAmount
------------- ------------- ----------- ------------- -----------
1 1 101 1 100
1 1 101 1 200
1 1 201 1 100
1 1 201 1 200
1 1 301 1 100
1 1 301 1 200
You can see that the values are duplicated by the number of matching rows in the other table so the SUM will be incorrect.

Related

How to get values that do not exist in another table, ignoring duplicate values with the most recent date

How to get only the LEVEL_20 values in table A that do not exist in table B given the ff. scenario:
If PRIZE with CODE_LEVEL level_20 has the same LEVEL_20 value in A, ignore it!
If there are multiple PRIZE values, compare the value with the max date in B
Table A
ALIAS LEVEL_20 POINTS DATE
Jbax 300 325 6/20/2018
Cvg2 100 103 6/20/2018
Deg1 200 281 6/20/2018
Table B
ALIAS CODE_LEVEL PRIZE DATE
Jbax Level_20 500 1/15/2017
Jbax Level_10 200 3/20/2017
Cvg2 Level_20 100 4/5/2018
Deg1
Cvg2 Level_20 50 2/1/2017
TABLE C
ALIAS NAME
Jbax Jessie
Cvg2 Carol
Deg1 Danny
Expected Output:
NAME RANKING
Danny 200
Jessie 300
My (wrong) code:
select name,
case when points between 101 and 200 then '100'
when points between 201 and 300 then '200'
when points between 301 and 400 then '300'
end ranking
from c left outer join b on b.alias = c.alias
left outer join a on a.alias = c.alias
where do not exists
(select 'x' from b where b.code_level = 'level_20'
and b.prize <> a.level_20
and b.alias = a.alias
and b.date =
(select max(b2.date)
from b b2 where b2.alias = b.alias
and b2.code_level = b.code_level)
)
This query adds sorting in table b:
select a.alias, a.level_20, c.name, b.code_level, b.prize, b.date_,
row_number() over (partition by a.alias, c.name order by b.date_ desc) rn
from a join c on c.alias = a.alias
left join b on b.alias = a.alias and b.code_level = 'Level_20'
Result:
ALIAS LEVEL_20 NAME CODE_LEVEL PRIZE DATE_ RN
----- ---------- ------ ---------- ---------- ----------- ----------
Cvg2 100 Carol Level_20 100 2017-04-05 1
Cvg2 100 Carol Level_20 50 2017-02-01 2
Deg1 200 Danny 1
Jbax 300 Jessie Level_20 500 2017-01-15 1
pqrs 100 Peter Level_20 200 2017-04-05 1
pqrs 100 Peter Level_20 100 2017-02-01 2
So now you can take only these rows which are newest (rn = 1) and have null prize or prize differs from a.level_20:
select name, level_20
from (
select a.alias, a.level_20, c.name, b.code_level, b.prize, b.date_,
row_number() over (partition by a.alias, c.name order by b.date_ desc) rn
from a join c on c.alias = a.alias
left join b on b.alias = a.alias and b.code_level = 'Level_20')
where rn = 1 and (prize is null or prize <> level_20)
dbfiddle demo (I added one row there)
Using this query without including the table b is enough due to your expected output and depending on data in your tables :
with a(ALIAS, LEVEL_20, POINTS, "DATE") as
(
select 'Jbax',300,325,date'2018-06-20' from dual union all
select 'Cvg2',100,103,date'2018-06-20' from dual union all
select 'Deg1',200,281,date'2018-06-20' from dual
), b(ALIAS, CODE_LEVEL, PRIZE, "DATE") as
(
select 'Jbax','Level_20',500,date'2017-01-15' from dual union all
select 'Jbax','Level_10',200,date'2017-03-20' from dual union all
select 'Cvg2','Level_20',100,date'2017-04-05' from dual union all
select 'Deg1',null,null,null from dual union all
select 'Cvg2','Level_20',50,date'2017-02-01' from dual
), c(ALIAS,NAME) as
(
select 'Jbax','Jessie' from dual union all
select 'Cvg2','Carol' from dual union all
select 'Deg1','Danny' from dual
)
select c.name, a.level_20
from c
join a on a.alias = c.alias
where (a.alias,a.level_20) not in
(select a.alias,b.prize
from a
join b on b.prize = a.level_20 and b.code_level = 'Level_20');
NAME LEVEL_20
------ --------
Jessie 300
Danny 200
P.S. There is no multiple PRIZE values in table b
Demo
This should give you the expected result:
with get_max_value as
(
select b2.*, max(b2."DATE") over (partition by b2.ALIAS) mdate from b b2
)
select my_c.name, my_a.level_20
from c my_c join a my_a on my_a.alias = my_c.alias
where (my_a.alias,my_a.level_20) not in
(select a2.alias, gmv.prize
from a a2 join get_max_value gmv on gmv.prize = a2.level_20
and gmv.code_level = 'Level_20'
WHERE gmv."DATE" = gmv.mdate);
I think you mean same ALIAS and not same price. In my head same price make no sense so i just group by same alias. If you need max price then the suggestion in other answer will do.

T-SQL (Azure) table gives double results

I am trying to get a list of all the id_user and the amount of creditRecieved and the amount of creditUsed, I actually would like to add a third option that mentions the left over if any. If creditReceived is bigger as creditUsed. But the first part now doubles the outputs so I started following an explanation here on StackOverflow, but I can't get it to work.
SELECT
dbo.UserPurchase.CreditUsed,
dbo.UserCredit.CreditRecieved
FROM
(
SELECT SUM(dbo.UserPurchase.creditUsed) AS CreditUsed
FROM dbo.UserPurchase
)
dbo.UserPurchase LEFT JOIN
(
SELECT SUM(dbo.UserCredit.creditRecieved) AS CreditRecieved
FROM dbo.UserCredit
)
dbo.UserCredit ON dbo.UserPurchase.id_user = dbo.UserCredit.id_user
The data comes from these two tables
TABLE dbo.UserCredit
id id_user creditRecieved PurchasePrice
----------- ----------- -------------- -------------
1 1 150 750
2 1 25 100
3 2 65 15
TABLE dbo.UserPurchase
id id_user creditUsed date
----------- ----------- ----------- -----------
1 1 175 NULL
2 2 3 NULL
3 2 2 NULL
I would like to have the following result
id_user CreditRecieved CreditUsed CreditLeftOver
----------- -------------- ----------- --------------
1 175 175 0
2 65 5 60
Try querying each table separately:
SELECT users.id_user, coalesce(credit.received, 0) received,
coalesce(purchase.used, 0) Used, coalesce(credit.received, 0) - coalesce(purchase.used, 0) LeftOver, coalesce(purchase.purchaseCount, 0) purchaseCount
FROM
(
select distinct id_user from dbo.UserCredit
union
select distinct id_user from dbo.UserPurchase
) users
left outer join
(
select id_user, sum(creditRecieved) received from dbo.UserCredit group by id_user
) credit
on users.id_user = credit.id_user
left outer join
(
select id_user, sum(creditUsed) used, count(1) purchaseCount from dbo.UserPurchase group by id_user
) purchase
on users.id_user = purchase.id_user
Please note that there is a typo in CreditRecieved. It should be CreditReceived.
If I understand you correctly, you want the creditUsed and creditRecieved (typo on your end?) summed up by id_user? This would be the query:
SELECT p.id_user, SUM(p.creditUsed), SUM(u.creditRecieved)
FROM dbo.UserCredit u
LEFT JOIN dbo.UserPurchase p ON p.id_user = u.id_user
GROUP BY p.id_user
You can use this query
SELECT
C.id_user,
C.CreditRecieved,
P.CreditUsed,
C.CreditRecieved - P.CreditUsed CreditLeftOver
FROM
(SELECT id_user, SUM(creditRecieved) CreditRecieved FROM UserCredit GROUP BY id_user) C
LEFT JOIN
(SELECT id_user, SUM(creditUsed) CreditUsed FROM UserPurchase GROUP BY id_user) P ON C.id_user = P.id_user

Group By syntax while using Cross Join

i wish to apply a Group By condition to my SQL below so that the O/P will display POL# with GP.
select t.POl#, (DEB.CUSTD - CRED.CUSTC) AS GP
from (
(
select POL.SP_NUM POL#
, sum(D.AMT) AS CUSTD
from S_INVOICE D
, S_ASSET POL
where POL.ROW_ID = D.FN_ACCNT_ID
and POL.SP_NUM in ('000','111','222')
and D.DEBIT_TYPE = 'Customer'
group by POL.SP_NUM
) DEB
CROSS JOIN
(
select pol.SP_NUM POL#
, sum(C.AMT) AS CUSTC
from S_SRC_PAYMENT C
, S_ASSET POL
where POL.ROW_ID = C.ASSET_ID
and POL.SP_NUM in ('000','111','222')
and C.CG_DEDN_TYPE_CD = 'Customer'
group by POL.SP_NUM
) CRED
) t
group by t.POL#
When i execute the same I am getting "ORA-00933: SQL command not properly ended" error, where the cursor points to 't'
Kindly assist.
Expected O/P:
POL# GP
000 800
111 120
222 50
Appending Sample Data with explanation for better understanding of requirement:
Table 1:
S_ASSET
ROW_ID POL#
1 000
2 111
3 222
4 333
5 444
Table 2:
S_INVOICE (Debit Table)
FN_ACCNT_ID POL# DEBIT_TYPE AMT
1 000 Customer 10
1 000 Customer 10
1 000 Insurer 5
2 111 Customer 10
3 222 Customer 10
3 222 Insurer 5
5 444 Insurer 10
Table 3:
S_SRC_PAYMENT (Credit Table)
ASSET_ID POL# CG_DEDN_TYPE_CD AMT
1 000 Insurer 10
1 000 Insurer 10
1 000 Customer 5
2 111 Insurer 10
3 222 Insurer 5
3 222 Insurer 5
3 222 Customer 5
5 444 Customer 10
As per this query I will consider "Customer" records per POL# and SUM the AMT. (Every Debit for the Customer will Credit Insurer(s) per POL# & vice-versa)
Expected O/P (Sum of Debits - Sum of Credits) for a Customer per POL#
POL # AMT (GP)
000 15
111 10
222 5
333 0
444 -10
You obviously only want to get the deb and cred per s_asset and then aggregate so as to get the sums. You can do this without a join and subquery the sums directly instead:
select
sp_num as pol#,
sum(<get deb sum for the pol.row_id here>) - sum(<get cred sum for the pol.row_id here>)
from s_asset pol
where sp_num in ('000','111','222')
group by sp_num;
The complete query:
select
sp_num as pol#,
coalesce(sum(
(
select sum(deb.amt)
from s_invoice deb
where deb.fn_accnt_id = pol.row_id
and deb.debit_type = 'Customer'
)
), 0) -
coalesce(sum(
(
select sum(cred.amt)
from s_src_payment cred
where cred.asset_id = pol.row_id
and cred.cg_dedn_type_cd = 'Customer'
), 0)
) as gp
from s_asset pol
where sp_num in ('000','111','222')
group by sp_num;
The same with joins instead:
select
pol.sp_num as pol#,
coalesce(sum(deb.total), 0) - coalesce(sum(cred.total), 0) as gp
from s_asset pol
left join
(
select fn_accnt_id as pol_row_id, sum(deb.amt) as total
from s_invoice
where debit_type = 'Customer'
group by fn_accnt_id
) deb on deb.pol_row_id = pol.row_id
left join
(
select asset_id as pol_row_id, sum(amt) as total
from s_src_payment
where cg_dedn_type_cd = 'Customer'
group by asset_id
) cred on cred.pol_row_id = pol.row_id
group by pol.sp_num;
This syntax
from ( (select ...) CROSS JOIN (select ...) )
is invalid. A JOIN belongs to a FROM. So either of these would be correct:
from ( SELECT * FROM (select ...) CROSS JOIN (select ...) )
or
from (select ...) CROSS JOIN (select ...)
However, are you sure you want a CROSS JOIN at all? Both subqueries give you POL numbers plus data, bat rather than joining them on POL#, you cross join the results, so you get all POL# combinations instead.

Compare Values Between Two Tables

I Have two tables, Table_A and Table_B.
I want result like the table result below, can someone help me with the SQL query?
Table_A
------------------
ID ITEM_ID QTY
------------------
1 100 2
2 101 3
3 102 5
4 103 2
------------------
Table_B
------------------
ID ITEM_ID QTY
1 100 2
2 101 4
3 102 4
4 104 2
5 105 1
------------------
RESULT
------------------
ITEM_ID QTY
100 0
101 1
102 -1
103 -2
104 2
105 1
------------------
Thanks.
You need a full join to get values from either table
select
isnull(a.item_id,b.item_id) as item_id, isnull(b.qty, 0) - isnull(a.qty, 0) as qty
from
table_a a
full outer join table_b b on a.item_id = b.item_id
select item_id, sum(qty)
from (select item_id, qty
from table_b
union all
select item_id, -qty
from table_a) x
group by item_id
order by item_id
Used full outer join and some isnulls
SQL Fiddle Example
select isnull(b.item_id, a.item_id) as ItemID,
isnull(b.qty, 0) - isnull(a.qty,0)as Qty
from table_b b
full outer join table_a a on a.item_id = b.item_id
order by itemid

T-SQL split row into multiple rows?

I have the table in sql db like this.
Category Series Value
1 A 100
2 B 200
2 C 300
How do I select to project like this?
Category Series Value
1 A 100
1 B 0
1 C 0
2 A 0
2 B 200
2 C 300
In order to get the result, you will want to generate a list of all categories with each series. You can use a CROSS JOIN to get the result:
select distinct c.category, s.series
from yourtable s
cross join yourtable c
Once you have this, then you can join this back to your table on both the category and series:
select sc.category,
sc.series,
coalesce(t.value, 0) value
from
(
select distinct c.category, s.series
from yourtable s
cross join yourtable c
) sc
left join yourtable t
on sc.series = t.series
and sc.category = t.category;
See SQL Fiddle with Demo