How to get two different total in two different tables in firebird? (Joining 3 Tables using left outer join) - sql

I have 3 tables and the sample data is listed below
Table #1 EMPLOYEE
EMPLOYEE ID EMPLOYEE_NAME
1 Juan Dela Cruz
2 Jobert Saver
Table #2 ADD_TABLE
ADD_TABLE_PK EMPLOYEE_ID ADD_AMOUNT
1 1 10.00
2 1 13.00
Table #3 SUBTRACT_TABLE
SUBTRACT_PK EMPLOYEE_ID SUBTRACT_AMOUNT
1 1 2.00
2 1 3.00
3 1 4.00
My desired output is just like this:
SELECT PROCEDURE
EMPLOYEE_ID EMPLOYEE_NAME TOTAL(Total = sum of ADD_TABLE.ADD_AMOUNT - sum of SUBTRACT_TABLE.SUBTACT_AMOUNT)
1 Juan Dela Cruz 14.00 ( = 23.00 - 9.00)
but the result of my select procedure is:
EMPLOYEE_ID EMPLOYEE_NAME TOTAL
1 Juan Dela Cruz 51.00
here is my select procuder using left outer join
CREATE PROCEDURE SAMPLE_SELECT
RETURNS(
EMPLOYEE_ID INTEGER,
EMPLOYEE_NAME VARCHAR(50) CHARACTER SET ISO8859_1 COLLATE ISO8859_1,
TOTAL DECIMAL(12, 2))
AS
BEGIN
FOR
SELECT
A.EMPLOYEE_ID,
A.EMPLOYEE_NAME,
SUM(B.ADD_AMOUNT) - SUM(C.SUBTRACT_AMOUNT)
FROM EMPLOYEE A
LEFT OUTER JOIN ADD_TABLE B ON A.EMPLOYEE_ID = B.EMPLOYEE_ID
LEFT OUTER JOIN SUBTRACT_TABLE C ON A.EMPLOYEE_ID = C.EMPLOYEE_ID
GROUP BY
A.EMPLOYEE_ID,
A.EMPLOYEE_NAME
INTO
:EMPLOYEE_ID,
:EMPLOYEE_NAME,
:TOTAL
DO
BEGIN
SUSPEND;
END
END;

You need to do the aggregation before joining the tables:
select e.*, coalesce(add_amount, 0) - coalesce(subtract_amount, 0)
from employee e left join
(select employee_id, sum(add_amount) as add_amount
from add_table a
group by employee_id
) a
on a.employee_id = e.employee_id left join
(select employee_id, sum(subtract_amount) as subtract_amount
from subtract_table s
group by employee_id
) s
on s.employee_id = e.employee_id;
Note: This will keep all employees. Your query attempt uses LEFT JOINs, so this does as well.

Tested with Firebird 2.5, more direct:
Select e1.employee_id, e1.employee_name,
(Select Coalesce(Sum(a1.add_amount), 0)
From add_table a1
Where a1.employee_id = e1.employee_id) -
(Select Coalesce(Sum(s1.subtract_amount), 0)
From subtract_table s1
Where s1.employee_id = e1.employee_id) As total
From employee e1

Related

An SQL query to pull count of employees absent under each manager on all dates

The objective of the query is get a count of employees absent under each manager.
Attendance (Dates when employees are present)
id date
1 16/05/2020
2 16/05/2020
1 17/05/2020
2 18/05/2020
3 18/05/2020
Employee
id manager_id
1 2
2 3
3 NA
The desired output should be in this format:
Date manager_id Number_of_absent_employees
16/05/2020 NA 1
17/05/2020 3 1
17/05/2020 NA 1
18/05/2020 2 1
I have tried writing code but partially understood it, intuition being calculating total number of actual employees under each manager and subtracting it from number of employees present on given day. Please help me in completing this query, many thanks!
with t1 as /* for counting total employees under each manager */
(
select employee.manager_id,count(*) as totalc
from employee as e
inner join employee on e.employee_id=employee.employee_id
group by employee.manager_id
)
,t2 as /* for counting total employees present each day */
(
select Attendence.date, employee.manager_id,count(*) as present
from employee
Left join Attendence on employee.employee_id=Attendence.employee_id
group by Attendence.date, employee.manager_id
)
select * from t2
Left join t1 on t2.manager_id=t1.manager_id
order by date
Cross join the distinct dates from Attendance to Employee and left join Attendance to filter out the matching rows.
The remaining rows are the absences so then you need to aggregate:
select d.date, e.manager_id,
count(*) Number_of_absent_employees
from (select distinct date from Attendance) d
cross join Employee e
left join Attendance a on a.date = d.date and a.id = e.id
where a.id is null
group by d.date, e.manager_id
See the demo.
Results:
| date | manager_id | Number_of_absent_employees |
| ---------- | ---------- | -------------------------- |
| 16/05/2020 | NA | 1 |
| 17/05/2020 | 3 | 1 |
| 17/05/2020 | NA | 1 |
| 18/05/2020 | 2 | 1 |
Try this query. In first cte just simplify your code. And in the last query calculate absent employees.
--in this CTE just simplify counting
with t1 as /* for counting total employees under each manager */
(
select employee.manager_id,count(*) as totalc
from employee
group by manager_id
)
,t2 as
(
select Attendence.date, employee.manager_id,count(*) as present
from employee
Left join Attendence on employee.employee_id=Attendence.employee_id
group by Attendence.date, employee.manager_id
)
select t2.date,t2.manager_id, (t1.totalc-t2.present) as employees_absent from t2
Left join t1 on t2.manager_id=t1.manager_id
order by date
Select ec.manager_id, date, (total_employees - employee_attended) as employees_absent from
(Select manager_id, count(id) as total_employees
from employee
group by manager_id) ec,
(Select distinct e.manager_id, a.date, count(a.id) over (partition by e.manager_id, a.date) as employee_attended
from Employee e, attendence, a
where e.id = a.id(+)) ea
where ec.manager_id = ea.manager_id (+)
I guess this should work

Select which value is greater based on two different columns

I would like to select which rate is greater and enter it in a single query result based on the results of a UNION ALL. For example employee 200 makes 25 dollars as a base rate per hour but the job he works on has a base rate of 10.00. He should be getting 25.00 per hour then. Employee 100 has a base rate of 10.00 but the job's base rate is 25.00. So he should get 25.00 per hour as well. I would like to select the highest rate for each employee. Something similar to this idea. SELECT EmployeeID, RATE_A or RATE_B from .... Here is some data I put together
CREATE Table WageRate(
[ID] [int] IDENTITY(1,1) NOT NULL,
[RateCode] int NULL,
[Rate] Decimal (10,2) NULL
)
INSERT INTO WageRate( RateCode,Rate)
Values (1,10.00), (2,15.00), (3,20.00), (4,25.00)
Create Table Employee(
[ID] [int] IDENTITY(1,1) NOT NULL,
[EmployeeID] int NULL,
[RateCode] int NULL
)
Insert Into Employee (EmployeeID,RateCode)
Values (100,1), (200,4)
Create Table TimeCards(
[ID] [int] IDENTITY(1,1) NOT NULL,
[EmployeeID] int NULL,
[Hours] Decimal (10,2) NULL,
[JobRateCode] int NULL
)
Insert Into TimeCards (EmployeeID,[Hours],JobRateCode)
Values (100,8.00,4), (200,8.00,1)
SELECT t1.Employeeid ,(t0.Rate) as [Rate_A] ,Null FROM WageRate t0
INNER JOIN Employee t1 ON t1.RateCode= t0.RateCode
INNER JOIN TimeCards t2 on t1.EmployeeID = t2.EmployeeID
UNION ALL
SELECT t4.Employeeid ,Null,(t3.Rate) As [Rate_B] FROM WageRate t3
INNER JOIN TimeCards t4 on t4.JobRateCode = t3.RateCode
INNER JOIN Employee t5 ON t4.EmployeeID= t5.EmployeeID
Using a case expression you can do it with a single query without a union:
SELECT e.EmployeeID,
CASE WHEN ISNULL(ew.Rate, 0.0) > ISNULL(jw.Rate, 0.0) THEN
ew.Rate
ELSE
jw.Rate
END As Rate
FROM Employee e
LEFT JOIN TimeCards t On e.EmployeeID = t.EmployeeID
LEFT JOIN WageRate ew ON e.RateCode = ew.RateCode
LEFT JOIN WageRate jw ON t.JobRateCode = jw.RateCode
See a live demo on rextester.
SELECT e.employeeid,
CASE
WHEN wr_tc.rate > wr_emp.rate
THEN wr_tc.rate
ELSE wr_emp.rate
END AS rate
FROM Employee e
INNER JOIN wagerate wr_emp ON wr_emp.ratecode = e.RateCode
INNER JOIN timecards tc ON tc.employeeid = e.employeeid
INNER JOIN wagerate wr_tc ON wr_tc.RateCode = tc.jobratecode
How about something like this...
SELECT
e.EmployeeID,
tc.Hours,
PayRate = CASE WHEN wr1.Rate > wr2.Rate THEN wr1.Rate ELSE wr2.Rate END,
TotalPay = tc.Hours * CASE WHEN wr1.Rate > wr2.Rate THEN wr1.Rate ELSE wr2.Rate END
FROM
#Employee e
JOIN #WageRate wr1
ON e.RateCode = wr1.RateCode
JOIN #TimeCards tc
ON e.EmployeeID = tc.EmployeeID
JOIN #WageRate wr2
ON tc.JobRateCode = wr2.RateCode;
Results...
EmployeeID Hours PayRate TotalPay
----------- --------------------------------------- --------------------------------------- ---------------------------------------
200 8.00 25.00 200.0000
100 8.00 25.00 200.0000
To do this without CASE logic:
SQL Fiddle
Query:
SELECT s.EmployeeID, s.Hours, s.jobRate, s.wageRate
, ( SELECT max(r) FROM (VALUES (s.jobRate),(s.wageRate)) AS value(r)) AS maxRate
FROM (
SELECT e.EmployeeID, t.Hours
, COALESCE(w1.Rate,0) AS jobRate
, COALESCE(w2.Rate,0) AS wageRate
FROM TimeCards t
INNER JOIN Employee e ON t.EmployeeID = e.EmployeeID
LEFT OUTER JOIN WageRate w1 ON e.RateCode = w1.RateCode
LEFT OUTER JOIN WageRate w2 ON t.jobRateCode = w2.RateCode
) s
;
Results:
| EmployeeID | Hours | jobRate | wageRate | maxRate |
|------------|-------|---------|----------|---------|
| 100 | 8 | 10 | 25 | 25 |
| 200 | 8 | 25 | 10 | 25 |

Case Statement With Multiple Joins

I have two tables, emp and location. I need to fetch the records for all the matching eid s' of emp table based on location type.
If the location type=2 then we need to fetch the city associated with it.
If we don't have type=2 record we need to fetch type=1 associated city for the matching eid.
My case statement works fine until there are two records for the eid of both type 1 and type 2. But I need to fetch only type 2 in this case
select case when a.type=2 then a.city
When a.type=1 then a.city
Else '0' End As City
From location a
Join emp r
On a.eid=r.eid
emp table
eid ename
1 james
2 mark
3 kristie
4 john
5 allen
location table
city eid type
athens 1 2
melbourne 2 1
london 2 2
newyork 3 1
output:
eid ename city type
1 james athens 2
2 mark london 2
3 kristie newyork 1
I think the most direct way to represent what you're asking for is:
select coalesce(l2.city, l1.city, '0') as city
From emp r
left join location l1
on l1.eid = r.eid
and l1.type=1
left join location l2
on l2.eid = r.eid
and l2.type=2
The subquery-based solution proposed by Jeremy Real may also work, but it assumes that 1 and 2 are they only values in the table for location.type (and I just don't find it to be as intuitive).
Try this:
select a.eid
,r.ename
,case when a.type=2 then b.city
when a.type=1 then b.city
else '0' End As City
from (
select a.eid, max(a.type) as type
From location a
group by a.eid
) a
right outer join location b
on a.eid = b.eid and a.type=b.type
inner join emp r
on b.eid=r.eid
You want to rank your cities. Use ROW_NUMBER to do that:
select e.eid, e.name, l.city, l.type
from emp e
join
(
select
city, eid, type,
row_number() over (partition by eid order by type desc) as rn
from location
) l on l.eid = e.eid and l.rn = 1;
rn is 1 for the better city per eid (where "better" is the one with the higher type).

SQL want Repeated result NULL from one table

hi i have problem in sql query example
Employee
empid empname
1 gan
2 sam
Designation
id desig empid
1 sr officerr 1
2 jr officer 1
3 manager 2
i want join tables and want Employee Table repeated records Null
result like
empid name desig id
1 gan sr officerr 1
1 NULL jr officer 2
2 sam manager 3
i working on query but i not getting result
SELECT DISTINCT designatin.empid, employee.empname,designatin.desig
FROM designatin INNER JOIN employee e ON employee.empid = designatin.empid
GROUP BY employee.empid, employee.empname, designatin.desig
can anybody have solution?
Change the inner join to a left join:
SELECT DISTINCT designatin.empid, employee.empname,designatin.desig
FROM designatin LEFT JOIN employee e ON employee.empid = designatin.empid
GROUP BY employee.empid, employee.empname, designatin.desig
Let try this, it will help you
SELECT e.empid, e.empname,d.desig ,d.id
FROM employee e
INNER JOIN Designation d ON e.empid = d.empid
See DEMO

Get all employee who directly or indirectly reports to an employee, with hierarchy level no

I have a Employee table like
emp_id bigint,
reports_to bigint,
emp_name varchar(20),
Constraint [PK_Emp] Primary key (emp_id),
Constraint [FK_Emp] Foreign key (reports_to) references [MSS].[dbo].[Emp]([emp_id])
emp_id reports_to emp_name
------ ------ --------------
1 null Sumanta
2 1 Arpita
3 null Pradip
4 1 Sujon
5 2 Arpan
6 5 Jayanti
I want to get all the employees that directly or indirectly reports to Sumanta or emp_id(1), and with hierarchy level, like this:
emp_id hierarchy_level emp_name
------ --------------- ----------
2 1 Arpita
4 1 Sujon
5 2 Arpan
6 3 Jayanti
I am new to SQL and just couldn't find what to use or how to get those results. Is it worth a stored procedure with table valued variable, or just a Tsql select query will be enough. Any help is most welcome.
All I have done is-
Select Ep.emp_id,ep.emp_eame
From Emp as E
Inner Join Emp as Ep on Ep.reports_to=E.Emp_id
Where E.reports_to=1 or E.emp_id=1;
but this is accurate upto 2 level and I cant even generate the hierarchy_level no.
Any suggestion, idea............ will be most helpfull.........
You could use a recursive CTE:
; with CTE as
(
select emp_id
, reports_to
, emp_name
, 1 as level
from Emp
where emp_name = 'Sumanta'
union all
select child.emp_id
, child.reports_to
, child.emp_name
, level + 1
from Emp child
join CTE parent
on child.reports_to = parent.emp_id
)
select *
from CTE
Example at SQL Fiddle.
Using Common_Table_Expression we can write like this
WITH Employee_CTE(employeeid,hierarchy_level,name) AS
(
SELECT employeeid,1 as level,name from employee where employeeid=1
UNION ALL
SELECT e.employeeid,level +1, e.name
from employee e
INNER JOIN Employee_CTE c ON e.employeeid = c.managerid
)
SELECT * FROM Employee_CTE order by employeeid