Joins and Loops in SQL Server 2008 - sql

How to join the below tables to get result like in "Result" table:
Table: Invoice
Inv_No Fk_Rep_ID Inv_Date Inv_Amt
3000 202 10/1/2014 35
3001 194 11/1/2014 40
3002 180 15/1/2014 55
Table :Return
Return_ID FK_Rep_ID Ret_Date Ret_Amt
2000 202 17/1/2014 67
2001 194 15/1/2015 43
Table: Credit
Credit_ID FK_Rep_ID credit_Date credit_Amnt
1000 NULL 4/2/2014 60
1001 202 5/2/2014 12
Table: Debit
Debit_ID FK_Rep_ID Debit_Date Debit_Amnt
400 NULL 4/5/2014 600
4001 194 5/5/2014 110
Table:Receipt_Items
Fk_Rec_No FK_Item_No Item_Type Rec_Item_ID
7787 1000 2 1
7788 2000 1 1
7788 3000 0 2
7788 3001 0 3
7788 3002 0 4
7788 4000 3 5
7788 4001 3 6
7789 1001 2 1
Table :Sales_Rep
Rep_ID Rep_Name
180 Vinu
194 Bibin
202 Salman
Result
Fk_Rec_No Fk_Item_No Item_Type Rep_Name Item_Date Item_Amt
7787 1000 Credit NULL 4/2/2014 -60
7788 2000 Return salman 15/1/2014 -67
7788 3000 Invoice salman 10/1/2014 35
7788 3001 Invoice Bibin 11/1/2014 40
7788 3002 Invoice Vinu 12/1/2014 55
7788 4000 Debit NULL 4/5/2014 600
7788 4001 Debit Bibin 5/5/2014 110
7789 1001 Credit Salman 5/2/2014 -12
Query :
SELECT tt.*,SR.Rep_Name
FROM(SELECT
fk_receipt_no
,fk_item_no
,CASE Item_type
WHEN 0 THEN 'INVOICE'
WHEN 1 THEN 'Return'
WHEN 2 THEN 'Credit'
WHEN 3 THEN 'Debit'
END as ITEM_type,
Case Item_type when 1 then '-'+Cast(Item_Amnt as varchar(50))
when 2 then '-'+Cast(Item_Amnt as varchar(50))
else Cast(Item_Amnt as varchar(50)) End Item_Amnt
,COALESCE(R.FK_Rep_ID,C.FK_Rep_ID,I.FK_Rep_ID) as FK_Rep_ID
,COALESCE(R.Ret_Date,C.Note_Date,I.Inv_Date) as Item_Date
FROM Recp_Item RI LEFT JOIN [Return] R ON RI.FK_Item_no=R.Return_ID
LEFT JOIN Credit C ON RI.FK_Item_No=C.Note_ID
LEFT JOIN Invoice I ON RI.FK_Item_No=I.Inv_No
) tt LEFT JOIN [Sales Rep] SR ON SR.Rep_ID=tt.FK_Rep_ID

You probably should mention that FK_Item_No refers sometimes to different things, like Credit_ID. This kind of multipurpose FK almost always results in a union for each type of item it could represent.
This is pseudo code, I expect you to provide all the tedious join criterias since you should be capable of doing so.
Notice left joins are used to the Rep table since you have some null FK's
In each select aliases are used to normalize the column names such as credit_amnt => Item_Amt. Technically this is only required for the first select in the join so long as the others are in the same order, but I usually do it for all unions for readability.
Select * From
(
Select ri.Fk_Rec_No, Credit_ID as Fk_Item_No, it.Name as Item_Type,
r.Rep_Name, c.credit_Date as Item_Date, c.credit_Amnt as Item_Amt
From Credit c inner join Receipt_Items ri left join Rep r
--join with your item type table you don't show
Union
Select ri.Fk_Rec_No, Debit_ID as Fk_Item_No, it.Name as Item_Type,
r.Rep_Name, c.Debit_Date as Item_Date, d.Debit_Amnt as Item_Amt
From Debit d inner join Receipt_Items ri left join Rep r
--join with your item type table you don't show
Union
...
) as typesUnion
Order By Fk_Rec_No, Fk_Item_No

Related

Sql code returns unexpected results. How to correct it

I'm using Firebird 3. There are 'sellers' master table for my contragent sellers, detailed 'Doc' table for goods income documents and subdetail table 'paym' for payments.
Seller: Seller_id, seller
Docs: doc_id, doc_summa, part_id
Paym: paid_id, doc_id, paid
Seller:
seller_id
seller
8
Firm1
45
Firm2
Docs:
doc_id
doc_summa
seller_id
346
1000
8
347
600
45
348
800
45
Paym:
paym_id
doc_id
paid
1
346
100
2
346
100
3
347
200
4
348
100
5
348
50
My aim is to get my residual debts(income-payment) like this:
seller_id
summa
paid
debt
8
1000
200
800
45
1400
350
1050
but get this multiplied, wrong summas:
seller_id
summa
paid
debt
8
2000
200
800
45
2200
350
1850
SELECT
s.seller_id,
sum(d.doc_summa) as summa,
sum(p.paid) as paid,
sum(d.doc_summa)-sum(p.paid) as debt
FROM seller s Left Join Docs d on s.seller_id=d.seller_id
Left Join paym p on p.doc_id= d.doc_id
GROUP BY s.seller_id
What is wrong in my SQL code?
The problem with your query is that because there are multiple payments for doc_ids 346 and 348, you will have two rows with a doc_summa of 1000 and 800 respectively, when you then sum them, the total becomes 2000 for seller_id 8 and 2200 for seller_id 45.
To solve this, you need to consolidate (sum) the payments before joining with docs. For example like the following:
with consolidated_paym as (
select
doc_id,
sum(paid) as total_paid
from paym
group by doc_id
)
select
s.seller_id,
sum(d.doc_summa) as summa,
sum(p.total_paid) as paid,
sum(d.doc_summa - p.total_paid) as debt
from seller s
left join docs d
on d.seller_id = s.seller_id
left join consolidated_paym as p
on p.doc_id = d.doc_id
group by s.seller_id
Fiddle: https://dbfiddle.uk/6_vyCu0w

How to find the record which is not exists with some criteria in SQL Server?

I have two tables.
ItemRelation table having 30k records
ID ChildID1 ChildID2 ChildID3
------------------------------------------
9 null null null
49 43 50 //43 in childid1, don't want this record too
111 112 113 null
65 68 null null
222 221 223 224
79 null null null
5773 5834 5838 null
F_ItemDailySalesParent having millions of records
ItemID StoreId
-----------------
9 1001 //ItemID 9,41,5773 belongs to 1001 StoreID
41 1001
43 1400 //ItemID 43,45,65,5834 belongs to 1400 StoreID
45 1400
65 1400
68 2000 //ItemID 68,79 belongs to 2000 StoreID
79 2000
5773 1001
5834 1400
5838 2000
I want to show the record ID from ItemRelation table where the ItemID from F_ItemDailySalesParent not present in ItemRelation
ItemID StoreID
-----------------
49 1001
111 1001
65 1001
222 1001
79 1001
9 1400
111 1400
222 1400
79 1400
9 2000
49 2000
111 2000
222 2000
5773 2000
I tried this following query. But this will work without StoreID. But no idea for the above result
select ID from HQMatajer.dbo.ItemRelation ir
where not exists(
select ID,StoreID
from [HQWebMatajer].[dbo].[F_ItemDailySalesParent] Fid
where fid.ItemID=ir.ID
or fid.ItemID = ir.ChildID1
or Fid.ItemID=ir.ChildID2
or Fid.ItemID=ir.ChildID3
and time between '2017-01-01 00:00:00.000' and '2017-02-28 00:00:00.000'
group by ItemID,StoreID
)
Update
I have Hqmatajer.dbo.Store that column name of storeCode = F_ItemDailySalesParent.Storeid
Include checking if StoreId matches when using the not exists()
select ID
from HQMatajer.dbo.ItemRelation ir
cross join (select distinct storeCode from Hqmatajer.dbo.Store) s
where not exists(
select 1
from [HQWebMatajer].[dbo].[F_ItemDailySalesParent] Fid
where fid.StoreId = s.StoreCode
and [time] between '2017-01-01 00:00:00.000' and '2017-02-28 00:00:00.000'
and ( fid.ItemID=ir.ID
or fid.ItemID=ir.ChildID1
or Fid.ItemID=ir.ChildID2
or Fid.ItemID=ir.ChildID3
)
)
If I understand correctly, you want to start with a list of all stores and items and then filter out the ones that are present.
select i.id, s.storeId
from (select distinct id from HQMatajer.dbo.ItemRelation ir) i cross join
stores s -- assume this exists
where not exists (select 1
from [HQWebMatajer].[dbo].[F_ItemDailySalesParent] idsp
where idsp.ItemID = i.ID and idsp.storeId = s.storeId
) and
not exists (select 1
from [HQWebMatajer].[dbo].[F_ItemDailySalesParent] idsp
where idsp.ItemID = i.childID1 and idsp.storeId = s.storeId
) and
not exists (select 1
from [HQWebMatajer].[dbo].[F_ItemDailySalesParent] idsp
where idsp.ItemID = i.childID2 and idsp.storeId = s.storeId
) and
not exists (select 1
from [HQWebMatajer].[dbo].[F_ItemDailySalesParent] idsp
where idsp.ItemID = i.childID3 and idsp.storeId = s.storeId
);
I did not include the time condition. It is not in your sample data, so it is unclear where it fits.
First get a unique list of ItemIds and unique list of StoreIDs, then you can see which are missing with a left join and a where cross ref table id is null. I'll do it in generic terms so you get the idea:
select s.StoreId, i.ItemId
from Stores s
cross apply Items i
left join ItemRelation ir
on s.StoreId = ir.StoreId
and i.ItemId = ir.ItemId
where ir.Id is null

Oracle SQL for commission per employee per week

Here is the sample data in an Oracle 11g database I am trying to work with to come up with a SQL query to return commission earned by each employee per week. If an employee did not earn a commission in a week, then it should display 0 as in the commission_earned column. (Assuming the week start on Monday)
SQL> select * from SALES;
EMPLID PRODUCT_TYPE PRODUCTID SALE_AMOUNT COMMISSION_EARNED SALE_DATE
1001 Desktop 55355251 750 45 02-MAY-16
1002 Desktop 2332134 600 30 02-MAY-16
1001 Laptop 773643 1200 65 02-MAY-16
1003 Camera 5546232 450 25 03-MAY-16
1002 Printer 445321 150 15 04-MAY-16
1001 Printer 445321 150 15 10-MAY-16
1003 Camera 5546232 450 25 10-MAY-16
I am trying to come up with a sql that would return the total commission earned by each employee per week. I would appreciate any help or pointers.
WEEKOF EMPLID COMMISSION_EARNED
02-MAY-16 1001 110
02-MAY-16 1002 45
02-MAY-16 1003 25
09-MAY-16 1001 15
09-MAY-16 1002 0
09-MAY-16 1003 25
I came up with the below sql but it does not display a row for emplid 1002 with commision_earned as 0 for the week starting with 09-MAY-16
SQL> select trunc(sale_date,'IW') WEEKOF,emplid,sum(COMMISSION_EARNED) COMMISSION_EARNED from sales group by trunc(sale_date,'IW'),emplid order by trunc(sale_date,'IW'),emplid;
WEEKOF EMPLID COMMISSION_EARNED
02-MAY-16 1001 110
02-MAY-16 1002 45
02-MAY-16 1003 25
09-MAY-16 1001 15
09-MAY-16 1003 25
You can generate all the rows with a cross join and then use left join to get the information from the table:
select w.wk, e.emplid,
coalesce(sum(t.commission_earned), 0) as commission_earned
from (select distinct trunc(weekof, 'IW') as wk from test) w cross join
(select distinct emplid from test) e left join
test t
on t.emplid = e.emplid and trunc(t.weekof, 'IW') = w.wk
group by w.wk, e.emplid
order by w.wk, e.emplid;

Oracle issue with reporting SQL

I have a reporting SQL in Oracle which is using the SUM function.
Table Y
CLIENT_NUM LOCATION_CODE EFFECTIVE_DATE EXPIRY_DATE
1234 AB01 1/1/2011 6/30/2014
1234 AB01 7/1/2014 8/31/2014
1234 AB01 9/1/2014 6/30/2015
1234 AB66 1/1/2011 6/30/2014
1234 AB66 7/1/2014 8/31/2014
1234 AB66 9/1/2014 6/30/2015
Table X
CLIENT_NUM LOCATION_CODE EMPLOYEE_COUNT TOTAL_HOURS_WORKED REPORTING_YEAR
1234 AB01 600 12000 2014
1234 AB66 50 11000 2014
SQL:
SELECT SUM(x.EMPLOYEE_COUNT)
OVER (PARTITION BY CASE ':input_parm:'
WHEN 'LOCATION' THEN y.LOCATION_CODE || ' ' || y.LOCATION_NAME
WHEN 'CLIENT' THEN y.CLIENT_NUM|| ' ' ||z.CLIENT_NAME
END
) AS EMPLOYEE_COUNT
, SUM(x.TOTAL_HOURS_WORKED)
OVER (PARTITION BY CASE ':input_parm:'
WHEN 'LOCATION' THEN y.LOCATION_CODE || ' ' || y.LOCATION_NAME
WHEN 'CLIENT' THEN y.CLIENT_NUM|| ' ' ||z.CLIENT_NAME
END
) AS TOTAL_HOURS_WORKED
FROM Y
LEFT OUTER JOIN X
ON Y.CLIENT_NUM = X.CLIENT_NUM
AND Y.LOCATION_CODE = X.LOCATION_CODE
AND X.REPORTING_YEAR = '2014'
AND location.EFFECTIVE_DATE BETWEEN TO_DATE('01-JAN-:in_reporting_year:','DD-MON-YYYY')
AND TO_DATE('31-DEC-:in_reporting_year:','DD-MON-YYYY')
WHERE Y.CLIENT_NUM='1234'
AND (Y.LOCATION_CODE='AB66' OR Y.LOCATION_CODE='AB01')
This is doubling the numbers up and returning the following when passing "CLIENT" or "LOCATION" for :input_parm: and 2014 for :in_reporting_year:
EMPLOYEE_COUNT TOTAL_HOURS_WORKED
1300 46000
1300 46000
This is the expected output:
EMPLOYEE_COUNT TOTAL_HOURS_WORKED
650 23000
650 23000
How can I further filter the join to match only one?
You have two rows in Y for the same location_code and client_num pairs that you're joining on. So the join is duplicating your results and thus the doubling in the totals.
I'm guessing you'll need to match on one of the dates values somehow.
EDIT: To address your question in comments you could try changing FROM Y to the following:
FROM (SELECT DISTINCT CLIENT_NUM, LOCATION_CODE FROM Y) as Y

Select statement with multiple rows from condition on values in single column affecting more than one column

I have the table below.Using salary as condition I want to get multiple rows. Below is current table call it employee.
empid | name | salary
-----------------------------------
1 A1 alex 20000
2 B2 ben 4500
3 C1 carl 14000
compare the salary to certain fixed values, and every time the salary is larger than the fixed value, show a record in output.Also output a calculated tax column.Below is a step closer to my final result
select e.*,tax= (case when salary<6000 then tax=0.06 *salary,when salary between 6000 and 18000 then tax= 0.06 *(salary -6000),else tax =0 ),m.incometype,
from employee e
left join
(
select 0 as threshold, 101 as incometype
union
select 5999 as threshold, 102 as incometype
union
select 17999 as threshold, 103 as incometype
) m
on e.salary > m.threshold
order by e.empid
Desired ouput would be:
empid | name | salary | incometype | tax
----------------------------------------------
1 A1 alex 20000 101 360
2 A1 alex 20000 102 720
3 A! alex 20000 103 0
4 B2 ben 4500 101 270
5 C1 carl 14000 101 360
6 C1 carl 14000 102 480
this is a question further to Select statement with multiple rows from condition on values in single column
this may help you
select e.*,
case
when salary<6000 then (0.06*salary)
when salary between 6000 and 1800 then (0.06*(salary -6000))
when m.incometype=103 then 0
end as tax
,m.incometype,
from employee e
left join
(
select 0 as threshold, 101 as incometype
union
select 5999 as threshold, 102 as incometype
union
select 17999 as threshold, 103 as incometype
) m
on e.salary > m.threshold
order by e.empid