Oracle issue with reporting SQL - 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

Related

Finding Duplicate Dates by a certain ID in a single table

Here is what my data looks like
ID Date Other Field
123 1/2/2017 a
123 1/3/2017 b
123 1/3/2017 c
123 1/5/2017 d
123 1/6/2017 e
123 1/6/2017 f
456 2/4/2017 g
456 2/4/2017 h
456 2/5/2017 i
456 2/6/2017 j
I am looking to identify when there is a date that is duplicated by ID, i would like to return the entire record. The results would look like
ID Date Other Field
123 1/3/2017 b
123 1/3/2017 c
123 1/6/2017 e
123 1/6/2017 f
456 2/4/2017 g
456 2/4/2017 h
I am not sure how to identify duplicate dates within a single table, then return those records only individually.
Any help is greatly appreciated!
select
t1.* from
table t1
join
(select id,date,count(*) from
table t2
group by id,date
having count(*)>=2
) t2
on t1.id=t2.id and t1.date=t2.date
What database are you using? If it supports window functions (e.g. Oracle or PostgreSQL), then:
select
id, "date", other_field
from
(
select
tbl.*,
count(*) over (partition by id, "date") date_cnt
from tbl
)
where date_cnt >= 2

SQL Updating two records

I am trying to update two employee's data. Let say one employee's id is 1234 and the other employee's id is 5678. Each employee has different services.
This is my table for reference
serv_id emp_id serv_name status
1 1234 computer1 A
5 1234 computer2 A
10 1234 computer3 A
37 1234 computer4 A
5 5678 computer2 A
11 5678 projector1 A
12 5678 projector2 A
30 5678 projector3 A
37 5678 computer4 A
35 5678 projector4 A
In the table above, we have 2 employees with 10 records and each employee has 5 services. What I want to do right now is to merge these two employee's services into one employee (1234) because employee 5678 is fired. However, serv_id with '5' or '37' does not need to be updated in employee 1234.
Here is my expected result. Let say A is active and I is inactive. Any suggestion on how to do it?
serv_id (pk) emp_id (fk) serv_name status
1 1234 computer1 A
5 (no update) 1234 computer2 A
10 1234 computer3 A
11 1234 projector1 A
12 1234 projector2 A
30 1234 projector3 A
35 1234 projector4 A
37 (no update) 1234 computer4 A
11 5678 projector1 I
37 5678 computer4 I
5 5678 computer2 I
11 5678 projector1 I
12 5678 projector2 I
30 5678 projector3 I
37 5678 computer4 I
35 5678 projector4 I
Try this
declare #tb as Table (serv_id int, emp_id int, serv_name nvarchar(20),status nvarchar(5))
insert into #tb
select 1 ,1234,'computer1','A' union
select 5 ,1234,'computer2','A' union
select 10,1234,'computer3','A' union
select 37,1234,'computer4','A' union
select 5 ,5678,'computer2','A' union
select 11,5678,'projector1','A' union
select 12,5678,'projector2','A' union
select 30,5678,'projector3','A' union
select 37,5678,'computer4','A' union
select 35,5678,'projector4','A'
update #tb set status ='I' where emp_id = 5678
insert into #tb select serv_id,1234,serv_name,'A' from #tb where emp_id=5678 and serv_id not in (select serv_id from #tb where emp_id=1234)
select * from #tb order by emp_id,serv_id
UPDATE t1
SET t1.status='I'
FROM my_table t1
LEFT OUTER JOIN my_table t2 ON t2.serv_name=t1.serv_name AND t2.emp_id=1234
WHERE t1.emp_id=5678
AND t2.serv_id IS NULL
We can join the table to itself by serv_name and emp_id to find common serv_names and then update only the records which have no same values for the emp_id=1234

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

Joins and Loops in SQL Server 2008

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

SQL Server : take 1 to many record set and make 1 record per id

I need some help. I need to take the data from these 3 tables and create an output that looks like below. The plan_name_x and pending_tallyx columns are derived to make one line per claim id. Each claim id can be associated to up to 3 plans and I want to show each plan and tally amounts in one record. What is the best way to do this?
Thanks for any ideas. :)
Output result set needed:
claim_id ac_name plan_name_1 pending_tally1 plan_name_2 Pending_tally2 plan_name_3 pending_tally3
-------- ------- ----------- -------------- ----------- -------------- ----------- --------------
1234 abc cooks delux_prime 22 prime_express 23 standard_prime 2
2341 zzz bakers delpux_prime 22 standard_prime 2 NULL NULL
3412 azb pasta's prime_express 23 NULL NULL NULL NULL
SQL Server 2005 table to use for the above result set:
company_claims
claim_id ac_name
1234 abc cooks
2341 zzz bakers
3412 azb pasta's
claim_plans
claim_id plan_id plan_name
1234 101 delux_prime
1234 102 Prime_express
1234 103 standard_prime
2341 101 delux_prime
2341 103 standard_prime
3412 102 Prime_express
Pending_amounts
claim_id plan_id Pending_tally
1234 101 22
1234 102 23
1234 103 2
2341 101 22
2341 103 2
3412 102 23
If you know that 3 is always the max amount of plans then some left joins will work fine:
select c.claim_id, c.ac_name,
cp1.plan_name as plan_name_1, pa1.pending_tally as pending_tally1,
cp2.plan_name as plan_name_2, pa2.pending_tally as pending_tally2,
cp3.plan_name as plan_name_3, pa3.pending_tally as pending_tally3,
from company_claims c
left join claim_plans cp1 on c.claim_id = cp1.claim_id and cp1.planid = 101
left join claim_plans cp2 on c.claim_id = cp2.claim_id and cp2.planid = 102
left join claim_plans cp3 on c.claim_id = cp3.claim_id and cp3.planid = 103
left join pending_amounts pa1 on cp1.claim_id = pa1.claimid and cp1.planid = pa1.plainid
left join pending_amounts pa2 on cp2.claim_id = pa2.claimid and cp2.planid = pa2.plainid
left join pending_amounts pa3 on cp3.claim_id = pa3.claimid and cp3.planid = pa3.plainid
I would first join all your data so that you get the relevant columns: claim_id, ac_name, plan_name, pending tally.
Then I would add transform this to get plan name and plan tally on different rows, with a label tying them together.
Then it should be easy to pivot.
I would tie these together with common table expressions.
Here's the query:
with X as (
select cc.*, cp.plan_name, pa.pending_tally,
rank() over (partition by cc.claim_id order by plan_name) as r
from company_claims cc
join claim_plans cp on cp.claim_id = cc.claim_id
join pending_amounts pa on pa.claim_id = cp.claim_id
and pa.plan_id = cp.plan_id
), P as (
select
X.claim_id,
x.ac_name,
x.plan_name as value,
'plan_name_' + cast(r as varchar(max)) as label
from x
union all
select
X.claim_id,
x.ac_name,
cast(x.pending_tally as varchar(max)) as value,
'pending_tally' + cast(r as varchar(max)) as label
from x
)
select claim_id, ac_name, [plan_name_1], [pending_tally1],[plan_name_2], [pending_tally2],[plan_name_3], [pending_tally3]
from (select * from P) p
pivot (
max(value)
for label in ([plan_name_1], [pending_tally1],[plan_name_2], [pending_tally2],[plan_name_3], [pending_tally3])
) as pvt
order by pvt.claim_id, ac_name
Here's a fiddle showing it in action: http://sqlfiddle.com/#!3/68f62/10