SQL server full join query - sql

I have a full join sql query and i am retrieving the data from the same table.the problem is i am getting the null value where i am expecting the column name.
Example:
I am having a table where there are two columns typeOfPost,dob.
DOB TypeOfPost
--------- --------------
20/11/1998 Manager
1/1/2000 Sales
13/6/1999 Manager
20/1/1987 Manager
1/11/1985 Sales
Now when I am writing a join query like
select DATENAME(month,dob) as Red,count(TypeOfPost)
from tablename
where TypeOfPost='Manager'
group by DATENAME(month,dob) as A
full join
select DATENAME(month,dob) as Green,count(TypeOfPost)
from tablename
where TypeOfPost='Sales'
group by DATENAME(month,dob) as B on B.Green = A.Red
Output-- Expected Output--
--------------------- ---------------------
Month Man Sal Month Man Sal
-------- ----- ------ -------- ----- ------
January 1 1 January 1 1
NULL 1 NULL June 1 NULL
November 1 1 November 1 1
Now here the problem rise, I want 'June' in the column Month instead of NULL value.
So is there any way to get that??
Help me out.
Thanks.

One option is to
use a CASE statement in a subselect
Determine for given record if it is a manager or sales
substitute with 1 or 0 accordingly
SELECT and GROUP from this subselect the final results.
SQL Statement
SELECT Month
, SUM(Man) AS Man
, SUM(Sal) AS Sal
FROM (
SELECT DATENAME(MONTH, DOB) AS Month
, CASE WHEN TypeOfPost = 'Manager' THEN 1 ELSE 0 END AS Man
, CASE WHEN TypeOfPost = 'Sales' THEN 1 ELSE 0 END AS Sal
FROM tableName
) g
GROUP BY
Month
or
SELECT Month
, SUM(Man)
, SUM(Sal)
FROM (
SELECT DATENAME(MONTH, DOB) AS Month
, COUNT(TypeOfPost) AS Man
, 0 AS Sal
FROM tableName
WHERE TypeOfPost = 'Manager'
GROUP BY
DATENAME(MONTH, DOB)
UNION ALL
SELECT DATENAME(MONTH, DOB) AS Month
, 0 AS Man
, COUNT(TypeOfPost) AS Sal
FROM tableName
WHERE TypeOfPost = 'Sales'
GROUP BY
DATENAME(MONTH, DOB)
) g
GROUP BY
Month

Related

PIVOT using JOIN in SQL

I was asked to pivot this data using basic SQL and wasn't sure how to answer it. I googled some answers and realized you can use MAX or SUM with CASE expressions, but at the end of the interview I asked how you would solve the question and the interviewer said by using joins. Can anyone show me how it's done using joins?
BEGINNING TABLE
emp_id
col_id
col_desc
attvalue
month
1
1
salary
2000
2010-05-09
1
2
bonus
0
2010-05-09
1
3
compensation
2000
2010-05-09
1
1
salary
2000
2010-05-10
1
2
bonus
500
2010-05-10
1
3
compensation
2500
2010-05-10
2
1
salary
1000
2010-05-09
2
2
bonus
500
2010-05-09
2
3
compensation
1500
2010-05-09
Code to create the beginning table
CREATE TABLE Employees(emp_id INT, col_id INT, col_desc NVARCHAR(MAX), attvalue INT, month DATE);
INSERT INTO Employees
VALUES
(1,1,'salary',2000,'2010-05-09'),
(1,2,'bonus',0,'2010-05-09'),
(1,3,'compensation',2000,'2010-05-09'),
(1,1,'salary',2000,'2010-05-10'),
(1,2,'bonus',500,'2010-05-10'),
(1,3,'compensation',2500,'2010-05-10'),
(2,1,'salary',1000,'2010-05-09'),
(2,2,'bonus',500,'2010-05-09'),
(2,3,'compensation',1500,'2010-05-09');
RESULTING TABLE
emp_id
month
salary
bonus
compensation
1
2010-05-09
2000
0
2000
1
2010-05-10
2000
500
2500
2
2010-05-09
1000
500
1500
Below are the self join, case expression and pivot way
-- Self Join way
select s.emp_id, s.month,
s.attvalue as salary,
b.attvalue as bonus,
c.attvalue as compensation
from Employees s
inner join Employees b on s.emp_id = b.emp_id
and s.month = b.month
inner join Employees c on s.emp_id = c.emp_id
and s.month = c.month
where s.col_desc = 'salary'
and b.col_desc = 'bonus'
and c.col_desc = 'compensation'
order by s.emp_id, s.month
-- case expression way
select emp_id, month,
max(case when col_desc = 'salary' then attvalue else 0 end) as salary,
max(case when col_desc = 'bonus' then attvalue else 0 end) as bonus,
max(case when col_desc = 'compensation' then attvalue else 0 end) as compensation
from Employees
group by emp_id, month
order by emp_id, month
-- Pivot way
select *
from (
select emp_id, month, col_desc, attvalue
from Employees
) d
pivot
(
max(attvalue)
for col_desc in ([salary], [bonus], [compensation])
) p
order by emp_id, month

query to retrieve changes in multiple columns in sql

I have the below table with the assignments details such as location, org, job, grade etc.
I want to build a query such that changes in the location, org are fetched for all system_person_type = 'EMP' only.
per_assignments
Person_id locat_id org_id job_id grade_id system_person_type START_DT END_DT
1 Toronto XYZ 1 GR1 EMP 01-JAN-2019 20-JAN-2019
1 US XYZ 1 GR1 EMP 21-JAN-2019 31-DEC-4712
2 Chicago ABC 2 GR1 EX-EMP 01-jul-2017 30-Nov-2017
2 Toronto XYZ 3 GR2 EMP 01-JAN-2019 03-JUL-2019
2 India GFH 3 GR2 EMP 04-JUL-2019 08-SEP-2019
2 India GFH 4 GR2 EMP 09-SEP-2019 31-DEC-4712
so in the above example the output should be :
person_id old_locat_id new_locat_id old_org_id new_org_id old_start_dt new_start_dat
1 Toronto US - - 01-jan-2019 21-jan-2019
2 Toronto India XYZ GFH 01-JAN-2019 04-JUL-2019
I created the below query But from the below query I am getting old_start_dt> new_start_dt and I am not getting all the changes required,
only 1 column change is retrieving. How can the below query be changed to accomadtae the above requirement ?
SELECT DISTINCT paam_change_loc.person_id ,
to_char(paam_change1.start_date,'YYYY-MM-DD') AS old_effective_start_dt ,
to_char(paam_change_loc.start_date,'YYYY-MM-DD') AS new_effective_start_dt ,
paam_change1.location_id AS old_loc_value ,
paam_change_loc.location_id AS new_loc_value
FROM per_assignments paam_change_loc,
per_assignments paam_change1
WHERE paam_change_loc.person_id =paam_change1.person_id
AND (
paam_change_loc.location_id IS NOT NULL
AND paam_change_loc.location_id <> paam_change1.location_id )
AND paam_change_loc.system_person_type = 'EMP'
AND paam_change1.system_person_type = 'EMP'
AND to_char(to_date(paam_change_loc.start_date),'DD-MM-YYYY') BETWEEN ('05-08-2019') AND '05-12-2019'
AND (
to_char(to_date(paam_change_loc.start_date)-1,'DD-MM-YYYY') BETWEEN ('05-08-2019') AND '05-12-2019' )
'05-08-2019' and '05-12-2019' is the transfer dates which will be passed to the query and the dates are to be compared in between these two dates
This query gives expected result:
select person_id, prev_start_dt, start_dt,
case loc_new when loc_old then ' - ' else loc_old end loc_old,
case loc_new when loc_old then ' - ' else loc_new end loc_new,
case org_new when org_old then ' - ' else org_old end org_old,
case org_new when org_old then ' - ' else org_new end org_new
from (
select person_id, locat_id loc_new, org_id org_new, start_dt,
lag(locat_id) over (partition by person_id order by start_dt) loc_old,
lag(org_id) over (partition by person_id order by start_dt) org_old,
lag(start_dt) over (partition by person_id order by start_dt) prev_start_dt,
case start_dt when 1 + lag(end_dt) over (partition by person_id order by start_dt)
then 1 end flag
from per_assignments)
where flag = 1 and (loc_new <> loc_old or org_new <> org_old)
dbfiddle
In the inner query apply filters for system_person_type and dates as needed. At first I used lag() three times and also to mark continuous rows, in column flag. Then only flagged rows are shown where location or organization changed.
I am not sure about data structure of your db. Considering the sample data as table, you can achieve the expected outptut using analytical function:
Select person_id,
Locat_id as old_locat_id,
New_locat_id,
org_id as old_org_id,
New_org_id,
Start_date as old_start_date,
New_start_date
From
(Select t.*,
Lead(org_id) over (partition by person_id order by start_date) as new_org_id,
Lead(start_date) over (partition by person_id order by start_date) as new_start_date,
Lead(locat_id) over (partition by person_id order by start_date) as new_locat_id,
From your_table t where system_person_type = 'EMP')
Where locat_id <> new_locat_id or org_id <> new_org_id;
Cheers!!

Grouping Data and maintaing Calculated fields

I have few fields like Department, WorkLocation, Employees and Work Date
Need to get the output based on the Given Date Parameter
Department WorkLocation EmployeeID WorkDate
---------- ------------ ---------- --------
D1 L2 121 05/01/2018
D1 L1 141 05/01/2018
D2 L1 151 05/02/2018
and so on
I have tried using temp tables to store data and get from there but is not working
Department , Number of Employees worked on the given date,Number of Employees worked in Last 7 days from the given Date,Number of Employees worked from the Begining of Month to the Given Date
i.e
Department WorkLocation Count_On_Date Count_Last7Days Count_MonthToDate
---------- ----------- ------------ --------------- -----------------
D1 L1 xxx xxx xxx
D2 L1 xxx xxx xxx
D1 L2 xxx xxx xxx
D2 L2 xxx xxx xxx
Please Help
Thanks
Use the GROUP BY clause and do the conditional aggregation with case expression
select Department, WorkLocation,
sum(case when WorkDate = #givendate then 1 else 0 end) as Count_On_Date,
sum(case when WorkDate >= dateadd(day, -8, #givendate) and
WorkDate < #givendate
then 1 else 0 end) as Count_Last7Days,
sum(case when month(WorkDate) = month(#givendate)
then 1 else 0 end) as Count_MonthToDate
from table t
group by Department, WorkLocation;
This I tested, works like expected. You need to use inline counts with different criteria. To produce each line we have case clauses with one-way conditions to cancel out the unwanted values.
select
wd.Department,
wd.WorkLocation,
COUNT(1) as Count_On_Date,
COUNT(case when DATEDIFF(DAY, wd.WorkDate, GETDATE()) < 7 then 1 end) as Count_Last7Days,
COUNT(case when DATEDIFF(DAY, wd.WorkDate, GETDATE()) < 30 then 1 end) as Count_MonthToDate
from WorkData as wd
group by wd.Department, wd.WorkLocation

Writing a request for the number of hired/fired each year

I'm trying to write an SQL request to count the number of Employees hired/fired each year.
I can have each Employee's dates with this select:
SELECT HiredDate, FiredDate FROM Employees;
I can list each year with this select:
SELECT to_char(e1.HiredDate, 'YYYY') Year FROM Employees e1
UNION
SELECT to_char(e2.FiredDate, 'YYYY') Year FROM Employees e2;
But I don't manage to count the number of hired/fired each year.
EDIT
Employees sample data:
Name | HiredDate | FiredDate
--------------------------------
John | 01/02/2003 | 03/04/2013
Jack | 05/06/2006 | 07/08/2013
Jean | 03/04/2006 | null
James | 01/02/2013 | null
Expected results:
Year | HiredNumber | FiredNumber
---------------------------------
2003 | 1 | 0
2006 | 2 | 0
2013 | 1 | 2
There may be years with no hiring and years with no firings. So the easiest way to solve this problem is with two sub-queries, one for each count and join them with a full outer join.
with e1 as (
select extract(year from hireddate) as emp_year
, count(hireddate) as hired_count
from employees
where hireddate is not null
group by extract(year from hireddate)
)
, e2 as (
select extract(year from fireddate) as emp_year
, count(fireddate) as fired_count
from employees
where fireddate is not null
group by extract(year from fireddate)
)
select coalesce (e1.emp_year, e2.emp_year) as emp_year
, nvl(e1.hired_count, 0) as hired_count
, nvl(e2.fired_count, 0) as fired_count
from e1
full outer join e2
on e1.emp_year = e2.emp_year
order by 1
Notes
This will exclude any years with neither hirings nor firings. It's easy enough to generate such things.
Presumably hireddate is mandatory but the not null check is retained for symmetry :)
". It works well in SQL Developer but can't be set as a Visual datasource"
Here is a variant without the FULL OUTER JOIN:
select emp_year
, sum(hired_count) as hired_count
, sum(fired_count) as fired_count
from (
select extract(year from hireddate) as emp_year
, count(hireddate) as hired_count
, 0 as fired_count
from employees
where hireddate is not null
group by extract(year from hireddate)
union all
select extract(year from fireddate) as emp_year
, 0 as hired_count
, count(fireddate) as fired_count
from employees
where fireddate is not null
group by extract(year from fireddate)
)
group by emp_year
order by 1
SELECT 'Hired' What, to_char(e1.HiredDate, 'YYYY') Year, COUNT(*) TheCount
FROM Employees e1
GROUP BY to_char(e1.HiredDate, 'YYYY')
UNION ALL
SELECT 'Fired' What, to_char(e2.FiredDate, 'YYYY') Year, COUNT(*) TheCount
FROM Employees e2
GROUP BY to_char(e2.FiredDate, 'YYYY');

#SQL - Order By matching record first

i need help to order this table (named "season") , by matching actual date with the BEGINDATE
ID NAME BEGINDATE
----------- -------------------- ----------
1 2014-2015 2014-10-01
2 2015-2016 2015-10-01
3 2016-2017 2016-10-01
4 2017-2018 2017-10-01
for example:
actual date is 2016/10/28 so we are in season 2016-2017 (id=3)
so the result should be
ID NAME BEGINDATE
----------- -------------------- ----------
3 2016-2017 2016-10-01
1 2014-2015 2014-10-01
2 2015-2016 2015-10-01
4 2017-2018 2017-10-01
UPDATE (SOLVED)
what i finally did was:
DECLARE #IDACTIVE AS INT = (SELECT MAX(ID) FROM SEASON WHERE BEGINDATE < GETDATE())
SELECT
1 AS ORDERBY,
ID,
NAME,
BEGINDATE
FROM SEASON
WHERE ID = #IDACTIVE
UNION
SELECT
2 AS ORDERBY,
ID,
NAME,
BEGINDATE
FROM SEASON
WHERE ID = #IDACTIVE
Follow the next approach:
1) Get The only matched row by using Top and Where clauses.
2) Get the all records except the one that you getting on point #1
3) Combine the result of two Selects via using UNION ALL.
Demo:-
Create table season (id int , NAME varchar(20),BEGINDATE date)
go
insert into season values (1,'2014-2015','2014-10-01')
insert into season values (2,'2015-2016','2015-10-01')
insert into season values (3,'2016-2017','2016-10-01')
insert into season values (4,'2017-2018','2017-10-01')
go
select * from (
select top 1 * from season
where BEGINDATE < getdate()
order by BEGINDATE desc
) a
union all
select * from season
where BEGINDATE != (
select top 1 BEGINDATE from season
where BEGINDATE < getdate()
order by BEGINDATE desc)
-- an another Soluation
select * from season
where DATEPART(Year,BEGINDATE) =DATEPART(Year,getdate())
union all
select * from season
where DATEPART(Year,BEGINDATE) !=DATEPART(Year,getdate())
The Result:
First move all future dates to the end, then order by beginDate
SELECT *
FROM season
ORDER BY CASE WHEN beginDate > GETDATE() THEN 0 ELSE 1 END,
beginDate
I think this is most easily done using window functions:
select s.*
from season s
order by (case when begindate = max(case when getdate() >= begindate then begindate end) over ()
then 1 else 2
end),
id