I have the following code. Is there another way to show ID in pivot besides what I have? What I have does not look very efficient:
create table #salary(
id int
, fNAme varchar(10)
, salary int
);
insert into #salary(id, fName, salary)
values(1,'jim',1000)
,(2,'mike',2000)
,(3,'tim',500)
,(1,'jim',300)
,(2,'mike',400)
,(3,'tim',250)
select 'salary' as salary, id, [jim], [mike], [tim]
from (
select id, fNAme, salary
from #salary
) x
PIVOT (
sum(salary) for fNAme in ([jim], [mike], [tim])
) as pvt
Output:
salary id jim mike tim
salary 1 1300 NULL NULL
salary 2 NULL 2400 NULL
salary 3 NULL NULL 750
If you are looking for an alternative approach you can consider conditional aggregation:
select
'salary' as salary
, id
, sum(case when fName = 'jim' then salary else null end) as jim
, sum(case when fName = 'mike' then salary else null end) as mike
, sum(case when fName = 'tim' then salary else null end) as tim
from
#salary
group by id
Result:
Related
I have a table that has entries like
Acct Nurse EntryDateTime DBCode Answer FormSeq
123 Sally 9/8/2020 09:22 Code1 Ans1 0001
123 Jim 9/8/2020 10:25 Code1 Ans2 0001
123 Sally 9/8/2020 09:15 Code2 C2Ans1 0001
I have a query that is pivoting this to get the answer from the last entry based on DBCode and EntryDateTime that works great. What I need to do is get the NURSE as well as the answer.
So my row would be
Acct Code1 Code1Nurse Code2 Code2Nurse
123 Ans2 Jim C2Ans1 Sally
Is there a way to do this? I would need the nurse for each unique DBCode
Here is my pivot code:
SELECT * FROM (
SELECT
[AcctNumber],
[Answer],
[DBCode],
[EntryDate],
[FormCode],[FormSeq]
FROM V_FAC_MULTIAPP_FORM_WITH_HOURLY
) MultiApp
PIVOT (
MAX( [Answer])
FOR [DBCode]
IN (
[AST],
[SDRM],
[SDRF],[SDAAS],[SDDCT],[SDDAS],[SDABY],[SDDAT],[SDTRCC],[SDADMTW],[Prptic],[Pdcrt]
)
) AS PivotTable WHERE EntryDate >='8/15/2020' and FormCode='LL003' ORDER BY EntryDate
You could use conditional aggregation.
Data
drop table if exists #tTEST;
go
select * INTO #tTEST from (values
(123, 'Sally', '9/8/2020 09:22', 'Code1', 'Ans1', '0001'),
(123, 'Jim', '9/8/2020 10:25', 'Code1', 'Ans2', '0001'),
(123, 'Sally', '9/8/2020 09:15', 'Code2', 'C2Ans1', '0001')) V(Acct, Nurse, EntryDateTime, DBCode, Answer, FormSeq);
Query
;with rn_cte as (
select *, row_number() over (partition by DBCode order by EntryDateTime desc) rn
from #tTEST)
select Acct,
max(case when DBCode='Code1' and rn=1 then Answer else null end) Code1,
max(case when DBCode='Code1' and rn=1 then Nurse else null end) Code1Nurse,
max(case when DBCode='Code2' and rn=1 then Answer else null end) Code2,
max(case when DBCode='Code2' and rn=1 then Nurse else null end) Code2Nurse
from rn_cte
group by Acct;
Output
Acct Code1 Code1Nurse Code2 Code2Nurse
123 Ans2 Jim C2Ans1 Sally
Here is something to play with. It would take some tinkering, but you could make the query dynamic to build out the columns to select based upon the DBCodes you have in your table...
IF OBJECT_ID('tempdb..#V_FAC_MULTIAPP_FORM_WITH_HOURLY') IS NOT NULL
DROP TABLE #V_FAC_MULTIAPP_FORM_WITH_HOURLY;
CREATE TABLE #V_FAC_MULTIAPP_FORM_WITH_HOURLY
(
Acct INT,
Nurse VARCHAR(20),
EntryDateTime DATETIME,
DBCode VARCHAR(10),
Answer VARCHAR(10),
FormSeq VARCHAR(10)
)
INSERT #V_FAC_MULTIAPP_FORM_WITH_HOURLY
VALUES
(123,'Sally','9/8/2020 09:22','Code1','Ans1','0001'),
(123,'Jim','9/8/2020 10:25','Code1','Ans2','0001'),
(123,'Sally','9/8/2020 09:15','Code2','C2Ans1','0001');
WITH Top_Row_Per_DBCode_By_EntryDate AS
(
SELECT ROW_NUMBER() OVER (PARTITION BY DBCode ORDER BY EntryDateTime DESC) AS top_row,
Acct,
DBCode,
Nurse+':'+Answer AS Answer
FROM #V_FAC_MULTIAPP_FORM_WITH_HOURLY
), Filtered_CTE AS
(
SELECT *
FROM Top_Row_Per_DBCode_By_EntryDate
WHERE top_row = 1
)
SELECT Acct,
Substring([Code1],CHARINDEX(':',[Code1])+1,LEN([Code1])-CHARINDEX(':',[Code1])) AS Code1,
Substring([Code1],0,CHARINDEX(':',[Code1])) AS Code1Nurse,
Substring([Code2],CHARINDEX(':',[Code2])+1,LEN([Code2])-CHARINDEX(':',[Code2])) AS Code2,
Substring([Code2],0,CHARINDEX(':',[Code2])) AS Code2Nurse
FROM Filtered_CTE
PIVOT
(
MAX( [Answer]) FOR [DBCode]
IN ([Code1],[Code2])
) AS PivotTable
One table
ID Name Email
101 George Georges1stEmail#hotmail.com
201 Fred Freds1stEmail#hotmail.com
301 Sally Sallys1stEmail#hotmail.com
101 George Georges2ndEmail#hotmail.com
101 George Georges3rdEmail#hotmail.com
301 Sally Sallys2ndEmail#hotmail.com
101 George Georges4thEmail#hotmail.com
101 George Georges5thEmail#hotmail.com
For the example below, I can't get the Email4 and Email5 fields to fit on this code page, but I'd like to either create another table that looks like this (with the addition of the Email4 and Email5):
ID Name Email1 Email2 Email3
101 George Georges1stEmail#hotmail.com Georges2ndEmail#hotmail.com Georges3rdEmail#hotmail.com
201 Fred Freds1stEmail#hotmail.com
301 Sally Sallys1stEmail#hotmail.com Sallys2ndEmail#hotmail.com
There can be a total of up to 5 emails for one ID.
Please and Thanks!
Everyone needs a little nudge from time-to-time.
If you have a known or maximum number of emails, you can perform a PIVOT in concert with the window function row_number()
Example
Select *
From (
Select ID
,Name
,EMail
,Item = 'Email'+left(row_number() over (partition by id order by email),2)
From YourTable
) src
Pivot (max(EMail) for Item in ([EMail1],[EMail2],[EMail3],[EMail4],[EMail5]) ) pvt
Returns
Or you can perform a Conditional Aggregration
Example
Select ID
,Name
,EMail1 = max(case when RN=1 then EMail else '' end)
,EMail2 = max(case when RN=2 then EMail else '' end)
,EMail3 = max(case when RN=3 then EMail else '' end)
,EMail4 = max(case when RN=4 then EMail else '' end)
,EMail5 = max(case when RN=5 then EMail else '' end)
From (
Select *
,RN = row_number() over (partition by id order by email)
From #YourTable
) src
Group By ID,Name
Returns
I have no idea if the title matches what I am trying to achieve in the query given below. The example might not be great but it serves what I am trying to achieve. There are three results that I am trying to get as shown below. Can this be done with a single query showing all the results in a better way?
TABLE:
name surname gender
arjun khadka female
arjun khadka male
arjun basnet male
kumar khadka female
kumar basnet female
arjun khadka female
arjun basnet female
kumar khadka female
query:
WITH cte
AS (SELECT
*,
DENSE_RANK() OVER (ORDER BY name, surname) AS groupid1,
DENSE_RANK() OVER (ORDER BY name, surname, gender) AS groupid2
FROM (SELECT
name,
surname,
gender
FROM name) x)
same person with duplicate gender as well as with non duplicate
gender
SELECT
name,
surname,
gender
FROM CTE
WHERE groupid1 IN (SELECT
groupid1
FROM cte
GROUP BY groupid1,
groupid2
HAVING COUNT(*) > 1)
AND groupid1 IN (SELECT
groupid1
FROM cte
GROUP BY groupid1,
groupid2
HAVING COUNT(*) = 1)
same person with no duplicate gender at all
SELECT
*
FROM cte
WHERE groupid1 NOT IN (SELECT
groupid1
FROM cte
GROUP BY groupid1,
groupid2
HAVING COUNT(*) > 1)
same person having duplicate gender record only
SELECT
*
FROM cte
WHERE groupid1 IN (SELECT
groupid1
FROM cte
GROUP BY groupid1,
groupid2
HAVING COUNT(*) > 1)
AND groupid1 NOT IN (SELECT
groupid1
FROM cte
GROUP BY groupid1,
groupid2
HAVING COUNT(*) = 1)
I modified what #uzi has provided, as solution as it has some flaws:
select
distinct name, surname,personCount as count
, personHasDupNUniqueGender = iif(min(allRowsCount) over (partition by personGroupId) = 1 and max (allRowsCount) over (partition by personGroupId) >1,1,0)
, personHasUniqueGender = iif(max(allRowsCount) over (partition by personGroupId) = 1,1,0)
, personHasAllDupGender = iif(personCount = allrowscount and personcount>1,1,0)
from (
select
*
, personGroupId= DENSE_RANK() over (order by name, surname)
, personCount = count(*) over (partition by name, surname)
, allRowsCount = count(*) over (partition by name, surname, gender)
from
name
) t
order by name, surname
Here's one way. Query returns all rows with extra columns condition_1, condition_2, condition_3. Values of those columns either 1 or 0. 1 - matches condition
declare #t table (
name varchar(100)
, surname varchar(100)
, gender varchar(100)
)
insert into #t values
('arjun', 'khadka', 'female')
,('arjun', 'khadka', 'male')
,('arjun', 'basnet', 'male')
,('kumar', 'khadka', 'female')
,('kumar', 'basnet', 'female')
,('arjun', 'khadka', 'female')
,('arjun', 'basnet', 'female')
,('kumar', 'khadka', 'female')
select
name, surname, gender
, condition_1 = max(iif(cnt_1 = 1 and cnt_2 > 2, 1, 0)) over (partition by name, surname)
, condition_2 = iif(cnt_2 - cnt_1 <= 1 and cnt_1 = 1, 1, 0)
, condition_3 = iif(cnt_2 = cnt_1 and cnt_1 > 1, 1, 0)
from (
select
*, cnt_1 = count(*) over (partition by name, surname, gender)
, cnt_2 = count(*) over (partition by name, surname)
from
#t
) t
order by name, surname
Output
name surname gender condition_1 condition_2 condition_3
-------------------------------------------------------------------
arjun basnet female 0 1 0
arjun basnet male 0 1 0
arjun khadka female 1 0 0
arjun khadka female 1 0 0
arjun khadka male 1 0 0
kumar basnet female 0 1 0
kumar khadka female 0 0 1
kumar khadka female 0 0 1
Edit:
select
name, surname, gender, cnt_1, cnt_2
, condition_1 = max(iif(cnt_1 = 1 and cnt_2 > 2, 1, 0)) over (partition by name, surname)
, condition_2 = iif(max(cnt_1) over (partition by name, surname) = 1, 1, 0)
, condition_3 = iif(cnt_2 = cnt_1 and cnt_1 > 1, 1, 0)
from (
select
*, cnt_1 = count(*) over (partition by name, surname, gender)
, cnt_2 = count(*) over (partition by name, surname)
from
#t
) t
order by name, surname
I am trying to find the second highest salary in each department.
Schema:
CREATE TABLE employees
(
ID int NOT NULL,
NAME char(50) NOT NULL,
departmentid int,
salary int
);
Sample records:
/*departmentid =1 */
INSERT INTO employees VALUES (1, 'Max', 1, 90000);
INSERT INTO employees VALUES (2, 'Joe', 1, 70000);
INSERT INTO employees VALUES (3, 'Randy', 1, 70000);
/*departmentid =2 */
INSERT INTO employees VALUES (4, 'Henry', 2, 80000);
INSERT INTO employees VALUES (5, 'SAM', 2, 60000);
/*departmentid =3 */
INSERT INTO employees VALUES (6, 'Janet', 3, 69000);
My query:
SELECT departmentid,
NAME,
salary
FROM
(
SELECT
departmentid,
NAME,
salary,
Dense_rank()OVER (partition BY departmentid
ORDER BY salary DESC) AS Rank,
Count(1)OVER(partition BY departmentid) AS cnt
FROM
employees
)t
WHERE
t.rank = 2
OR ( t.rank = 1
AND cnt = 1 )
The output I am getting is as below;
departmentid NAME salary
1 Joe 70000
1 Randy 70000
2 SAM 60000
3 Janet 69000
My expected output is
departmentid NAME salary
1 Joe 70000
1 Randy 70000
2 SAM 60000
3 NULL NULL
As there is only one record for departmentid=3, it should return null.
What is wrong with this query? Any other ways to achieve this result?
I've also included a SQL fiddle.
ROW_NUMBER() and select = 2
;WITH salary AS
(
[RN] = SELECT ROW_NUMBER() OVER (PARTITION BY departmentid ORDER BY salary),*
FROM <table>
)
SELECT
*
FROM salary
WHERE [RN] = 2
I've used two CTEs.
The first returns a list of every department. You'll need this to ensure departments with less than 2 salaries are included in the final result.
The second ranks each employee within their department.
Finally, I've used a left outer join to maintain the complete list of departments.
WITH Department AS
(
-- Returns a list of the departments.
SELECT
departmentid
FROM
employees
GROUP BY
departmentid
),
EmployeeRanked AS
(
SELECT
DENSE_RANK() OVER (PARTITION BY departmentid ORDER BY salary DESC) AS [Rank],
departmentid,
NAME,
salary
FROM
employees
)
SELECT
er.Rank,
d.departmentid,
er.NAME,
er.salary
FROM
Department AS d
LEFT OUTER JOIN EmployeeRanked AS er ON er.departmentid = d.departmentid
AND er.[Rank] = 2
;
Returns
Rank departmentid NAME salary
2 1 Joe 70000
2 1 Randy 70000
2 2 SAM 60000
(null) 3 (null) (null)
Use a sub query as i wrote here : http://sqlfiddle.com/#!6/bb5e1/26
with ranks as(
SELECT departmentid,
salary,
row_number() over (partition by (departmentid) order by salary desc) as rank
FROM employees
)
Select *
from ranks
Where ranks.rank = 2
If the departmentid having only one row, and if you consider that also. Then
Query
;with cte as(
select [rank] = dense_rank() over(
partition by departmentid
order by departmentid, salary desc
), *
from employees
)
select ID, NAME, departmentid, salary from cte
where [rank] = 2
union all
select max(ID), max(NAME), departmentid, max(salary)
from cte
group by departmentid
having count([rank]) = 1;
There is also a simple way:
SELECT TOP 1 * FROM (Select top 2 * FROM employees order by salary desc ) e Order by salary asc
Edit: this returns only the 2nd highest overall
I think you can get correct answer by just removing below code from your code
OR ( t.rank = 1
AND cnt = 1 )
also main table should be left join from this result to get null in rest of columns
i need help in below issue.i have a customer table CustA which is having columns custid, first name , surname, phone1, phone2,lastupdateddate. This table has duplicate records.a record is considered duplicate in CustA table when
first name & surname & (phone1 or phone2) is duplicated
custid firstname surname phone1 phone2 lastupdateddate
1000 Sam Son 334566 NULL 1-jan-2016
1001 sam son NULL 334566 1-feb-2016
i have used cte for this scenario to Partition by firstname, lastname, phone1, phone2 based on rownumber. But the OR condition is remaining as challenge for phone1 or phone2 in CTE query. Please share your thoughts. Appreciate it.
Trick here is COALESCE
With cte as
(
select Count()over(partition by firstname, lastname, coalesce(phone1, phone2)) as cnt,*
From yourtable
)
Select * from CTE
WHere cnt > 1
Though if it isn't the case that one is always null You can use a CASE expression to ensure that the values are presented in a consistent order.
WITH cte
AS (SELECT COUNT(*)
OVER(
partition BY firstname,
lastname,
CASE WHEN phone1 < phone2 THEN phone1 ELSE phone2 END,
CASE WHEN phone1 < phone2 THEN phone2 ELSE phone1 END) AS cnt,
*
FROM yourtable)
SELECT *
FROM CTE
WHERE cnt > 1
This one will also give you the list of dupes (optional custid<>A.custid)
Declare #Yourtable table (custid int,firstname varchar(50),surname varchar(50),phone1 varchar(25),phone2 varchar(25),lastupdate date)
Insert into #Yourtable values
(1000,'Sam','Son' ,'334566',NULL ,'1-jan-2016'),
(1001,'sam','son' ,NULL ,'334566','1-feb-2016'),
(1003,'sam','son' ,NULL ,NULL ,'2-feb-2016'),
(1002,'Not','ADupe',NULL ,NULL ,'1-feb-2016')
Select A.*
,B.Dupes
From #YourTable A
Cross Apply (Select Dupes=(Select Stuff((Select Distinct ',' + cast(custid as varchar(25))
From #YourTable
Where custid<>A.custid
and firstname=A.firstname
and surname =A.surname
and (IsNull(A.phone1,'') in (IsNull(phone1,''),IsNull(phone2,'')) or IsNull(A.phone2,'') in (IsNull(phone1,''),IsNull(phone2,'')) )
For XML Path ('')),1,1,'')
)
) B
Where Dupes is not null
Returns
custid firstname surname phone1 phone2 lastupdate Dupes
1000 Sam Son 334566 NULL 2016-01-01 1001,1003
1001 sam son NULL 334566 2016-02-01 1000,1003
1003 sam son NULL NULL 2016-02-02 1000,1001