Pivot on SQL Server 2010 - sql-server

I want to return the top three pets for each employee as columns instead of rows i.e.
Owner_ID Pet
--------------------
1 Cat
1 Dog
1 Hamster
2 Cow
2 Sheep
3 Dog
Convert it to
Owner_ID Pet1 Pet2 Pet3
-------------------------------------
1 Cat Dog Hamster
2 Cow Sheep null
3 Dog null null
The name of pets come from a lookup table and there can be any number of pets but I only want to return the top 3.
Here is my query:
SELECT Owner,Pet1, Pet2,Pet3
FROM
(select distinct OwnerID as Owner,glcom.Value as Pets
from Owner ,OwnerPets ,Pet
where Pet.Type='Furry'
and OwnerPets.OwnerID = OwnerID.OwnerID
and OwnerPets.PetID = Pet.PetID ) AS SourceTable
PIVOT
(
Max(Pets)
FOR Pets IN (Pet1, Pet2,Pet3)
) AS PivotTable;
Unfortunately it only returns null for each row... so the output I see is
Owner_ID Pet1 Pet2 Pet3
-------------------------------------
1 null null null
2 null null null
3 null null null
Hopefully it is a common problem and someone must have solved it already.
Thanks

If I have understood you correctly you can use ROW_NUMBER() over (partition by Owner_ID order by ...) on your query then pivot using those.
Example follows.
WITH Unpivoted AS
(
SELECT X.*, ROW_NUMBER() over (partition by Owner_ID order by Pet) AS RN
FROM (VALUES
(1, 'Cat') ,
(1, 'Dog') ,
(1, 'Hamster') ,
(1, 'Rhino'),
(1, 'Zebra'),
(2, 'Cow') ,
(2, 'Sheep') ,
(3, 'Dog' )
) AS X (Owner_ID, Pet)
)
SELECT Owner_Id, [1] AS Pet1, [2] AS Pet2,[3] AS Pet3 FROM Unpivoted
PIVOT
(
Max(Pet)
FOR RN IN ([1], [2],[3])
) AS PivotTable;
Returns
Owner_Id Pet1 Pet2 Pet3
----------- ------- ------- -------
1 Cat Dog Hamster
2 Cow Sheep NULL
3 Dog NULL NULL

Related

Count unique field when another field is all NULL

I have a table with three columns, which can contain duplicate rows
org - int NULL
id - int NULL
complete - bit NULL
So I might have data like so:
org | id | complete
-------------------
1 | 1 | 1
1 | 2 | NULL
1 | 2 | 1
1 | 3 | 1
2 | 3 | 1
2 | 4 | 1
2 | 4 | NULL
I want to get a count of all distinct id by org. That's easy enough to do with a COUNT(DISTINCT id) expression. Where I'm running into trouble now is I also want a count of all distinct id where any of the complete values isn't 1.
So from the above I'd want this output:
org | distinct id | distinct incomplete id
------------------------------------------
1 | 3 | 1
2 | 2 | 1
So for org 2, because id of 4 included a NULL value, then I can't count id 4 as fully complete, thus just id 3 is complete, thus resulting in a 1 in the distinct incomplete id column. So I don't know how to fill in the ???? part of the below query.
SELECT org, COUNT(DISTINCT id) TotalPeople, ???? IncompletePeople
FROM table
GROUP BY org
Try the following approach
DECLARE #T TABLE
(
Org INT,
Id INT,
Complete BIT
)
INSERT INTO #T
VALUES(1,1,1),(1,2,NULL),(1,2,1),(1,3,1),(2,3,1),(2,4,1),(2,4,NULL)
SELECT
Org,
DistinctId = COUNT(DISTINCT Id),
DistinctIncompleteId = SUM(CASE Complete WHEN 1 THEN 0 ELSE 1 END)
FROM #T
GROUP BY Org
You may try this way
create table #temptable ( org int, id int, comp int )
Go
insert into #temptable ( org, id, comp )
values ( 1,1,1)
,( 1, 2, null)
,( 1, 2, 1)
,( 1, 3, 1)
,( 2, 3, 1)
,( 2, 4, null)
,( 2, 4, 1)
select d.org, d.DistinctId, f.incompleteId from (
select COUNT (distinct id) as DistinctId , org from #temptable group by org) as d full outer join (
select COUNT (distinct id) as incompleteId , org from #temptable where comp is null group by org) as f on d.org=f.org
go
drop table #temptable
Group it by "org" and by "complete". Then put HAVING complete=1. Hope the code below helps you:
SELECT org, COUNT(id) TotalPeople, complete
FROM table
GROUP BY org,complete
HAVING complete=1 (complete IS NULL *for incomplete*)

How to join multiple tables , one table as dynamic columns and the other table as the values of these columns

I'm new to SQL Server pivot, and i'm trying to solve a problem where i need to output the following tables into one table that includes the values based on one table's columns
Here's my tables
ContactGroup
Title ID
---------- -----------
Group A 1
ContactsInGroups
ContactId GroupId
----------- -----------
1 1
2 1
3 1
ContactVariables
ID Name GroupId Order
----------- ---------- ----------- ------
1 Invoice 1 1
2 Due Date 1 1
ContactsVariablesValues
ContactVariableId ContactId Value
----------------- ----------- -----
1 1 600
Desired output
GroupId ContactId Invoice Due Date
----------- ----------- ----------- -----------
1 1 600 NULL
1 2 NULL NULL
1 3 NULL NULL
Ali, here is an example that will at least get you started. You can run the following in SSMS.
Create some table variables and insert your sample data.
DECLARE #ContactGroup TABLE ( id INT, title VARCHAR(50) );
INSERT INTO #ContactGroup ( id, title ) VALUES ( 1, 'Group A' );
DECLARE #ContactsInGroup TABLE ( ContactID INT, GroupID INT );
INSERT INTO #ContactsInGroup ( ContactID, GroupID ) VALUES ( 1, 1 ), ( 2, 1 ), ( 3, 1 );
DECLARE #ContactVariables TABLE ( id INT, [name] VARCHAR(50), GroupID INT, [Order] INT );
INSERT INTO #ContactVariables ( id, [name], GroupID, [Order] ) VALUES ( 1, 'Invoice', 1, 1 ), ( 2, 'Due Date', 1, 1 );
DECLARE #ContactsVariablesValues TABLE ( ContactVariableID INT, ContactID INT, [value] INT );
INSERT INTO #ContactsVariablesValues ( ContactVariableID, ContactID, [value] ) VALUES ( 1, 1, 600 );
Then query the data as follows:
SELECT
ContactGroup.id AS GroupID
, ContactsInGroup.ContactID
, ContactVars.Invoice
, ContactVars.[Due Date]
FROM #ContactGroup AS ContactGroup
INNER JOIN #ContactsInGroup AS ContactsInGroup
ON ContactGroup.id = ContactsInGroup.GroupID
OUTER APPLY (
SELECT
[Invoice], [Due Date]
FROM (
SELECT
Vars.[name]
, Vals.[value]
FROM #ContactVariables AS Vars
LEFT OUTER JOIN #ContactsVariablesValues Vals
ON Vars.id = Vals.ContactVariableID
WHERE
Vars.GroupID = 1
AND Vals.ContactID = ContactsInGroup.ContactID
) AS ContactData
PIVOT (
MIN( [value] )
FOR [name] IN (
[Invoice], [Due Date]
)
) AS pvt
) AS ContactVars
ORDER BY
ContactGroup.id, ContactsInGroup.ContactID;
Which returns:
+---------+-----------+---------+----------+
| GroupID | ContactID | Invoice | Due Date |
+---------+-----------+---------+----------+
| 1 | 1 | 600 | NULL |
| 1 | 2 | NULL | NULL |
| 1 | 3 | NULL | NULL |
+---------+-----------+---------+----------+
Things to note
The "magic" here is in the OUTER APPLY. This allows us to query a subset of data based on the primary data returned, in this case the GroupID and ContactID. OUTER APPLY will also return rows with NULL values like you desire.
You're going to have some challenges here, namely that to use a PIVOT as shown in my example, you will need to know all the values ( Invoice, Due Date, etc... ) that will become column headers. Based on your setup, I'm thinking this may not be the case, so you will be forced to resort to an technique that creates and executes a dynamic PIVOT statement for you within the OUTER APPLY.
You also might consider using a TABLE VALUED FUNCTION that does the PIVOT work that can then be JOINed on vs. an OUTER APPLY.
You have several options, but hopefully this helps jumpstart your thinking.

parent id hierarchy identification MS SqlServer2012

I have this code
create table #temp
(
order_id int not null identity(1,1) primary key
,sid int
,created_date date
,parent_order_id int
)
insert into #temp
(
sid
,created_date
)values(1,'2017-01-01')
insert into #temp
(
sid
,created_date
,parent_order_id
)values(1,'2017-02-01',1),(1,'2017-03-01',2),(1,'2017-04-01',3)
insert into #temp
(
sid
,created_date
)values(1,'2017-06-01')
insert into #temp
(
sid
,created_date
,parent_order_id
)values(1,'2017-07-01',5),(1,'2017-08-01',6)
select * from #temp
Whenever parent_order_id is null which indicates it is a new order. After that customer can add items associated to that order. so we have parent_order_id filled for these associations. But I want to know what is the first order_id for each association child order.I am looking for an output like below.
`order_id sid created_date parent_order_id original_order_id
1 1 2017-01-01 NULL 1
2 1 2017-02-01 1 1
3 1 2017-03-01 2 1
4 1 2017-04-01 3 1
5 1 2017-06-01 NULL 4
6 1 2017-07-01 5 4
7 1 2017-08-01 6 4
`
any help is appreciated. Thanks in advance.
With the following piece of code you can get results you are expecting.
;WITH cte (order_id, original_order_id)
AS
(
SELECT order_id, order_id AS original_order_id
FROM #temp WHERE parent_order_id IS NULL
UNION ALL
SELECT o.order_id AS order_id, cte.original_order_id AS original_order_id
FROM #temp AS o
JOIN cte
ON o.parent_order_id = cte.order_id
)
SELECT #temp.order_id, #temp.sid, #temp.created_date, #temp.parent_order_id, cte.original_order_id
FROM #temp
JOIN cte ON cte.order_id=#temp.order_id
ORDER BY cte.order_id
Please be aware, that there are certain limits on recursion as this for CTE. Currently it is 100 which can be pushed up to 32767.

SQL Server: query table filter by columns

I have a table that I am trying to query. It has two fields that UNIQUE Ids. For this example, I am going to use PersonA, PersonB, PersonC, PersonD. The table represents a relationship between two people.
Person Relationship table:
Row FieldId_01 FieldId_02
------------------------------
1 PersonA PersonB
2 PersonA PersonC
3 PersonB PersonA
4 PersonC PersonA
5 PersonD PersonA
Person table:
PersonID
---------
PersonA
PersonB
PersonC
PersonD
I don't care about the order, I just need the unique combination in reference to PersonA. So, row 1 and row 3 are the same, row 2 and 4 are the same, row 5 has no match, but still a unique combination.
I need to select a single unique combination.
Expected output should be
Person Relationship table
Row FieldId_01 FieldId_02
-------------------------------
1 PersonA PersonB
2 PersonA PersonC
5 PersonD PersonA
This will do what you are looking for. I included the sample data in a temp table so you can test it immediately:
CREATE TABLE #T (id int not null PRIMARY KEY, companyName varchar(16) not null)
INSERT INTO #t Values
(1, 'dogs ltd'),
(2, 'cats ltd'),
(3, 'pigs ltd'),
(4, 'pigs ltd'),
(5, 'cats ltd'),
(6, 'cats ltd'),
(7, 'dogs ltd'),
(8, 'pigs ltd')
SELECT id, CompanyName
FROM (
SELECT *,
LEAD(CompanyName, 1) OVER(ORDER BY id) as nc,
LAG(CompanyName, 1) OVER(ORDER BY id) AS pc
FROM #t t
) x
WHERE nc = companyName
OR pc = companyName
My Output:
id CompanyName
3 pigs ltd
4 pigs ltd
5 cats ltd
6 cats ltd
Using not exists():
If FieldId_01 is never the same as FieldId_02 in the same row, then:
select *
from t
where not exists (
select 1
from t as i
where i.Row < t.Row
and i.FieldId_01 in (t.FieldId_01,t.FieldId_02)
and i.FieldId_02 in (t.FieldId_01,t.FieldId_02)
)
otherwise
select *
from t
where not exists (
select 1
from t as i
where i.Row < t.Row
and ( (i.FieldId_01 = t.FieldId_01 and i.FieldId_02 = t.FieldId_02)
or (i.FieldId_02 = t.FieldId_01 and i.FieldId_01 = t.FieldId_02)
)
)
rextester demo: http://rextester.com/SQL61250
both return:
+-----+------------+------------+
| Row | FieldId_01 | FieldId_02 |
+-----+------------+------------+
| 1 | PersonA | PersonB |
| 2 | PersonA | PersonC |
| 5 | PersonD | PersonA |
+-----+------------+------------+

SQL Query Conversation

I have three tables:
__Person__ __Hat__ __Shoe__
ID int ID int ID int
Name nvarchar Name nvarchar Name nvarchar
HatID int
ShoeID int
Here's some sample data of the tables:
________________Person_________________
-ID- -Name- -HatID- -ShoeID-
1 Anna 1 2
2 Nina 2 3
3 Lola 3 NULL
______Hat_______
-ID- -Name-
1 Blue
2 Red
3 Green
______Shoe_______
-ID- -Name-
1 Boot
2 Heels
3 Sport
I have a query like this:
SELECT Person.ID, Person.Name, Hat.Name, Shoe.Name
FROM Person
INNER JOIN Hat ON Person.HatID = Hat.ID
JOIN JOIN Shoe ON Person.Shoe = Shoe.ID
This query returns the following results:
-PersonID- -PersonName- -HatName- -ShoeName-
1 Anna Blue Heels
2 Nina Red Sport
3 Lola Green NULL
I want to give PersonName, HatName and ShoeName order numbers 1, 2, 3. I need results like this:
-PersonID- -OrderNumber- -Value-
1 1 Anna
1 2 Blue
1 3 Heels
2 1 Nina
2 2 Red
2 3 Sport
3 1 Lola
3 2 Green
3 3 NULL
How should I write the query to return this results?
You can use UNPIVOT here:
DECLARE #Persons TABLE
(
ID INT ,
Name NVARCHAR(20) ,
HatID INT ,
ShoeID INT
)
DECLARE #Hats TABLE ( ID INT, Name NVARCHAR(20) )
DECLARE #Shoes TABLE ( ID INT, Name NVARCHAR(20) )
INSERT INTO #Persons
VALUES ( 1, 'Anna', 1, 2 ),
( 2, 'Nina', 2, 3 ),
( 3, 'Lola', 3, NULL )
INSERT INTO #Hats
VALUES ( 1, 'Blue' ),
( 2, 'Red' ),
( 3, 'Green' )
INSERT INTO #Shoes
VALUES ( 1, 'Boot' ),
( 2, 'Heels' ),
( 3, 'Sport' );
WITH cte
AS ( SELECT p.ID ,
p.Name AS PersonName ,
h.Name AS HatName ,
ISNULL(s.Name, '') AS ShoeName
FROM #Persons p
INNER JOIN #Hats h ON p.HatID = h.ID
LEFT JOIN #Shoes s ON p.ShoeID = s.ID
)
SELECT ID AS PersonID ,
ROW_NUMBER() OVER ( PARTITION BY ID ORDER BY ( SELECT
1
) ) AS OrderNumber ,
IIF(Value = '', NULL, Value) AS Value
FROM cte UNPIVOT( Value FOR v IN ( [PersonName], [HatName], [ShoeName] ) ) u;
Output:
PersonID OrderNumber Value
1 1 Anna
1 2 Blue
1 3 Heels
2 1 Nina
2 2 Red
2 3 Sport
3 1 Lola
3 2 Green
3 3 NULL
try this:
SELECT Person.ID, 1 OrderNumber, Person.Name
FROM Person
UNION ALL
SELECT Person.ID, 2 OrderNumber, Hat.Name
FROM Person
LEFT JOIN JOIN Hat ON Person.HatID = Hat.ID
UNION ALL
SELECT Person.ID, 3 OrderNumber, Shoe.Name
FROM Person
LEFT JOIN JOIN Shoe ON Person.ShoeID = Shoe.ID
ORDER BY 1,2

Resources