SQL Server 2005 Pivot Table question - sql

I have an Entity-Value set of tables, and would like to pivot the results.
Here's the effect that I'm looking for, except you see that the SELECT stmt using Common Table Expressions isn't working exactly right yet.
My question is: Am I on the right track, or is there some sort of easy pivot command?
USE TempDB
Declare #Person TABLE(
PersonID Int Identity(101,1))
INSERT INTO #Person DEFAULT VALUES
INSERT INTO #Person DEFAULT VALUES
INSERT INTO #Person DEFAULT VALUES
INSERT INTO #Person DEFAULT VALUES
DECLARE #Attribute TABLE(
AttributeID Int Identity(10,1) PRIMARY KEY,
AttributeName Varchar(MAX))
INSERT INTO #Attribute(AttributeName) VALUES('Firstname')
INSERT INTO #Attribute(AttributeName) VALUES('Lastname')
DECLARE #Pat TABLE( -- A Person's Attributes
PatID Int Identity,
PersonID Int,
AttributeID Int,
PatValue Varchar(MAX)
)
INSERT INTO #Pat(PersonID,AttributeID,PatValue) VALUES(101,10,'John')
INSERT INTO #Pat(PersonID,AttributeID,PatValue) VALUES(102,10,'Paul')
INSERT INTO #Pat(PersonID,AttributeID,PatValue) VALUES(103,10,'George')
INSERT INTO #Pat(PersonID,AttributeID,PatValue) VALUES(104,10,'Ringo')
INSERT INTO #Pat(PersonID,AttributeID,PatValue) VALUES(101,11,'Lennon')
INSERT INTO #Pat(PersonID,AttributeID,PatValue) VALUES(102,11,'McCartney')
INSERT INTO #Pat(PersonID,AttributeID,PatValue) VALUES(103,11,'Harrison')
SELECT Pat.PersonID,AttributeName,PatValue
FROM #Pat Pat
INNER JOIN #Person Person
ON Pat.PersonID = Person.PersonID
INNER JOIN #Attribute Attribute
ON Pat.AttributeID=Attribute.AttributeID
--
;WITH CTE1 AS(
SELECT PersonID,PatValue AS FirstName
FROM #Pat Pat
INNER JOIN #Attribute Attribute
ON Pat.AttributeID=Attribute.AttributeID
WHERE AttributeName='FirstName'
)
,CTE2 AS(
SELECT PersonID,PatValue AS LastName
FROM #Pat Pat
INNER JOIN #Attribute Attribute
ON Pat.AttributeID=Attribute.AttributeID
WHERE AttributeName='LastName'
)
SELECT Pat.PersonID,FirstName,LastName
FROM #Pat Pat
LEFT OUTER JOIN CTE1
ON Pat.PersonID=CTE1.PersonID
LEFT OUTER JOIN CTE2
ON Pat.PersonID=CTE2.PersonID
ORDER BY PersonID
I just want a list of the 4 people, with their firstname and lastname.

Good example Here
Query in your case would look like this:
SELECT PersonId, FirstName, LastName
FROM
(
-- your query
SELECT Pat.PersonID,AttributeName,PatValue
FROM #Pat Pat
INNER JOIN #Person Person
ON Pat.PersonID = Person.PersonID
INNER JOIN #Attribute Attribute
ON Pat.AttributeID=Attribute.AttributeID
) up
PIVOT (MAX(PatValue) FOR AttributeName IN (FirstName, LastName)) AS pvt

Related

Find duplicate names with different dob tsql (T-SQL)

I want to get the only duplicate names with different dob with dob in the result set. I don't want non-duplicate rows.
I tried CTE and combination of Group By and Having.
declare #person table (id int, e_name nvarchar(50), dob datetime)
INSERT #person VALUES (1,'Jack Hughens','1960-11-02')
INSERT #person VALUES (2,'Tom Hughens','1971-01-08')
INSERT #person VALUES (3,'Sam Scallion','1960-11-02')
INSERT #person VALUES (4,'Sam Scallion','1960-11-01')
INSERT #person VALUES (5,'Paul Darty','1994-10-19')
INSERT #person VALUES (6,'Paul Ashley','1983-09-21')
The result should be as below:
--------------------------------
|id|e_name |dob |
--------------------------------
|3|Sam Scallion |1960-11-02 |
--------------------------------
|4|Sam Scallion |1960-11-01 |
--------------------------------
If id is auto then you can do :
select p.*
from #person p
where exists (select 1 from #person p1 where p1.e_name = p.e_name and p.id <> p1.id);
However, you can also use dob instead of id :
select p.*
from #person p
where exists (select 1 from #person p1 where p1.e_name = p.e_name and p.dob <> p1.dob);
My suggestion uses GROUP BY with a HAVING-clause:
The mockup table to simulate your issue
declare #person table (id int, e_name nvarchar(50), dob datetime)
INSERT #person VALUES (1,'Jack Hughens','19601102')
INSERT #person VALUES (2,'Tom Hughens', '19710108')
INSERT #person VALUES (3,'Sam Scallion','19601102')
INSERT #person VALUES (4,'Sam Scallion','19601101')
INSERT #person VALUES (5,'Paul Darty', '19941019')
INSERT #person VALUES (6,'Paul Ashley', '19830921');
--The query
WITH FindNames AS
(
SELECT p.e_name
FROM #person p
GROUP BY p.e_name
HAVING COUNT(DISTINCT p.dob)>1
)
SELECT p.*
FROM #person p
INNER JOIN FindNames n ON p.e_name=n.e_name;
the idea in short:
Using COUNT(DISTINCT p.dob) will count differing values only.
The CTE will return names, where you see more than one, but different DOBs.
The final SELECT will JOIN the CTE's set, hence using it as filter.

Query different type data in to a single row with the master record

I just created the following sample data to demonstrate what I am after.
I need to query the above table to get my information as 1 single row
i.e. like the following
I was planning to create two temp tables and insert the two different types of addresses seperately. Then, inner join them with the main company table. I am not sure, this is a good solution. I appreaciate if anyone share their thoughts or code to my problem.
Try this..
Select companyId,CompanyName,homesddress1
,homeaddress2,HomePostCode,OfficeAddress1,OfficeAddress2,OfficePostCode
From tblCompany a
Outer apply ( select address1 homesddress1, address2 homeaddress2,postcode HomePostCode
From tblAddress t
Where AddressType='home' and t.companyid=a.companyid)
Outer apply (select address1 OfficeAddress1, address2 Officeaddress2,postcode OfficePostCode
From tblAddress t2
Where AddressType='Office ' and t2.companyid=a.companyid)
You can do it with a simple select using two outer joins. Note that you need the joins to be outer because for some companies you may only have one address.
DECLARE #company TABLE (
CompanyId int,
CompanyName varchar(50)
)
DECLARE #companyAddress TABLE (
Id int,
AddressType varchar(10),
Address1 varchar(50),
Address2 varchar(50),
Postcode varchar(10),
CompanyId int
)
INSERT INTO #company VALUES (1, 'Test Company')
INSERT INTO #companyAddress VALUES (1, 'Home', '25 Street', 'City 1', 'BA3 1PE', 1)
INSERT INTO #companyAddress VALUES (2, 'Office', '25 Street', 'City 2', 'NA1 4TW', 1)
SELECT c.CompanyId, c.CompanyName,
h.Address1 AS HomeAddress1, h.Address2 AS HomeAddress2, h.Postcode AS HomePostcode,
o.Address1 AS OfficeAddress1, o.Address2 AS OfficeAddress2, o.Postcode AS OfficePostcode
FROM #company c
LEFT JOIN
#companyAddress h ON h.CompanyId = c.CompanyId AND h.AddressType = 'Home'
LEFT JOIN
#companyAddress o ON o.CompanyId = c.CompanyId AND o.AddressType = 'Office'
Here is an Almost Dynamic version. You just have to specify the field list in the final pivot
Declare #YourTable table (ID int,AddressType varchar(25),Address1 varchar(50),Address2 varchar(50),CompanyID int)
Insert Into #YourTable values
(1,'Home' ,'25 Street','City 1',1),
(2,'Office','10 Avenue','City 2',1)
Declare #XML xml = (Select * from #YourTable for XML RAW) --<<< Initial Query
;with cteBase as (
Select ID = R.value('#CompanyID','int') --<<< Key ID
,AddressType = R.value('#AddressType','varchar(50)')
,Item = R.value('#AddressType','varchar(50)')+Attr.value('local-name(.)','varchar(100)')
,Value = Attr.value('.','varchar(max)')
From #XML.nodes('/row') as A(R)
Cross Apply A.r.nodes('./#*[local-name(.)!="CompanyID"]') as B(Attr) --<<< Key ID
),cteDist as (Select Distinct ID,Item from cteBase
),cteComp as (
Select A.*,B.Value
From cteDist A
Cross Apply (Select Value=Stuff((Select Distinct ',' + Value
From cteBase
Where ID=A.ID
and Item=A.Item
For XML Path ('')),1,1,'') ) B
)
Select *
From (Select * From cteComp) as s
Pivot (max(value)
For Item in (HomeID,HomeAddressType,HomeAddress1,HomeAddress2,OfficeID,OfficeAddressType,OfficeAddress1,OfficeAddress2)) as pvt
Returns
ID HomeID HomeAddressType HomeAddress1 HomeAddress2 OfficeID OfficeAddressType OfficeAddress1 OfficeAddress2
1 1 Home 25 Street City 1 2 Office 10 Avenue City 2

Restrict SQL output to items found only in subquery

Ok, given this example, can you point me in the right direction to restrict the output of this query to results found only in the where clause. I'll explain more..
SELECT
ServerName=substring(MN.object_text,patindex ('%Caption %',MN.object_text)+11,patindex('%"%',substring(MN.object_text,patindex ('%Caption %',MN.object_text)+11,80))-1)
/* ,MN.object_text */, p.name
FROM
[openview].[dbo].[sto_ov_managednode] MN
left outer join [openview].[dbo].[OV_PM_PolicyOnNode] PON
on pon.nodeid = mn.name
left outer join [openview].[dbo].[OV_PM_PolicyVersion] PV
on PV.policyversionid = pon.policyversionid
left outer join [openview].[dbo].[OV_PM_Policy] P
on P.PolicyId = pv.policyid
where p.name not in (select Name from dbo.OV_PM_Policy where PolicyId = any(select PolicyId from dbo.OV_PM_PolicyGroupAssignment where GroupId = '18681abc-097d-41cd-9782-e28d9a9f5fc4'))
and substring(MN.object_text,patindex ('%AgentBinaryFormatId%',MN.object_text)+22,1) <> '0' /* exclude non-managed nodes */
and substring(MN.object_text,patindex ('%OsVersionId%',MN.object_text)+14,2) = '18' /* include only Windows OS devices */
ORDER BY ServerName, name;
Lets say this returns all the servernames with a p.name not in my query. That's what it is supposed to do as written... but I only want that servername if it doesn't have one of those policies...and only if it doesn't have one of those... not listing other policies it may have.. but aren't in that result set... I've tried different joins, and used not in.... exists not exists... the issue.. is, when I use those.. it doesn't really change anything.
Think of a folder structure..... that p.name is looking in a folder and finding all the policy names inside. There are many folders and many other policies.... but in this I only care about those in that folder. So if server A has all the policies in that folder I don't want to see it.. If it has other policies I don't care... but if it's missing even one of the policies from that given folder I want to know about it.
So basically I want the output it is giving me currently.. but only if the policy found was in that group to begin with. So I need to restrict my output to only return servers missing policies from that group.
Updated -- sqlfiddle information and better example...thanks Mark for the sqlfiddle setup
create table sto_ov_managednode (
object_text varchar(100),
name varchar(100)
);
create table OV_PM_PolicyOnNode (
nodeid varchar(100),
policyversionid varchar(100)
);
create table OV_PM_PolicyVersion (
policyversionid varchar(100) unique,
policyid varchar(100)
);
create table OV_PM_Policy (
Name varchar(100),
PolicyId varchar(100)
);
create table OV_PM_PolicyGroupAssignment (
PolicyId varchar(100),
GroupId varchar(100),
PolicyVerisonId varchar(100) unique
);
insert into sto_ov_managednode values ('Caption ---Server1"---AgentBinaryFormatId---OsVersionId---18--','node1');
insert into sto_ov_managednode values ('Caption ---Server2"---AgentBinaryFormatId---OsVersionId---18--','node2');
insert into OV_PM_PolicyOnNode values ('node1','policyversionID1');
insert into OV_PM_PolicyOnNode values ('node1','policyversionID4');
insert into OV_PM_PolicyOnNode values ('node2','policyversionID1');
insert into OV_PM_PolicyVersion values ('policyversionid1','policyid1');
insert into OV_PM_PolicyVersion values ('policyversionid2','policyid1');
insert into OV_PM_PolicyVersion values ('policyversionid3','policyid2');
insert into OV_PM_PolicyVersion values ('policyversionid4','policyid2');
insert into OV_PM_PolicyVersion values ('policyversionid5','policyid3');
insert into OV_PM_PolicyVersion values ('policyversionid6','policyid3');
insert into OV_PM_Policy values ('policy A','policyid1');
insert into OV_PM_Policy values ('policy B','policyid2');
insert into OV_PM_Policy values ('policy C','policyid3');
insert into OV_PM_PolicyGroupAssignment values ('policyid1','Base Windows','policyversionid2');
insert into OV_PM_PolicyGroupAssignment values ('policyid2','Base Windows','policyversionid4');
insert into OV_PM_PolicyGroupAssignment values ('policyid3','Extra Windows','policyversionid5');
Now the output is this..
nodeName policyName
node1 policy C
node2 policy B
node2 policy C
You'll notice that this is basically correct.. but I don't want to see Policy C as it's not in the Base Windows Group.... I only need to see a policy if it's missing from a server...AND is included in the group I used to search for..
New SqlFiddle
Final Update
This is the final sql statement that did in fact work. Thanks for the effort, I appreciate it.
select ServerName=substring(MN.object_text,patindex ('%Caption %',MN.object_text)+11,patindex('%"%',substring(MN.object_text,patindex ('%Caption %',MN.object_text)+11,80))-1), "Policy" = P.name
FROM OV_PM_PolicyGroupAssignment PGA
left join OV_PM_Policy P on PGA.PolicyId = P.PolicyId
left join OV_PM_PolicyVersion PV on P.PolicyId = PV.policyid
cross join sto_ov_managednode MN
where PGA.GroupId = '18681abc-097d-41cd-9782-e28d9a9f5fc4'
and substring(MN.object_text,patindex ('%AgentBinaryFormatId%',MN.object_text)+22,1) <> '0' /* exclude non-managed nodes */
and substring(MN.object_text,patindex ('%OsVersionId%',MN.object_text)+14,2) = '18' /* include only Windows OS devices */
except
select ServerName=substring(MN.object_text,patindex ('%Caption %',MN.object_text)+11,patindex('%"%',substring(MN.object_text,patindex ('%Caption %',MN.object_text)+11,80))-1), P.name as policyName
from sto_ov_managednode MN
left join OV_PM_PolicyOnNode PON on pon.nodeid = mn.name
left join OV_PM_PolicyVersion PV on PV.policyversionid = pon.policyversionid
left join OV_PM_Policy P on P.PolicyId = pv.policyid
left join OV_PM_PolicyGroupAssignment PGA on P.PolicyId = PGA.PolicyId
where PGA.GroupId = '18681abc-097d-41cd-9782-e28d9a9f5fc4'
Order by ServerName, Policy;
I hope I understood your schema enough to correctly respond.
SQLFiddle: http://sqlfiddle.com/#!6/baffd/35
Set up data:
create table sto_ov_managednode (
object_text varchar(100),
name varchar(100)
);
create table OV_PM_PolicyOnNode (
nodeid varchar(100),
policyversionid varchar(100)
);
create table OV_PM_PolicyVersion (
policyversionid varchar(100),
policyid varchar(100)
);
create table OV_PM_Policy (
name varchar(100),
PolicyId varchar(100)
);
create table OV_PM_PolicyGroupAssignment (
PolicyId varchar(100),
GroupId varchar(100)
);
insert into sto_ov_managednode values ('Caption ---Server1"---AgentBinaryFormatId---OsVersionId---18--','node1');
insert into sto_ov_managednode values ('Caption ---Server2"---AgentBinaryFormatId---OsVersionId---18--','node2');
insert into OV_PM_PolicyOnNode values ('node1','policyversion1');
insert into OV_PM_PolicyOnNode values ('node1','policyversion2');
insert into OV_PM_PolicyOnNode values ('node2','policyversion1');
insert into OV_PM_PolicyVersion values ('policyversion1','policyid1');
insert into OV_PM_PolicyVersion values ('policyversion2','policyid2');
insert into OV_PM_Policy values ('policy A','policyid1');
insert into OV_PM_Policy values ('policy B','policyid1');
insert into OV_PM_Policy values ('policy C','policyid2');
insert into OV_PM_PolicyGroupAssignment values ('policyid1','18681abc-097d-41cd-9782-e28d9a9f5fc4');
insert into OV_PM_PolicyGroupAssignment values ('policyid2','18681abc-097d-41cd-9782-e28d9a9f5fc4');
Query:
select MN.name as nodeName, P.name as policyName
FROM OV_PM_PolicyGroupAssignment PGA
left join OV_PM_Policy P on PGA.PolicyId = P.PolicyId
left join OV_PM_PolicyVersion PV on P.PolicyId = PV.policyid
cross join sto_ov_managednode MN
where PGA.GroupId = 'Base Windows'
except
select MN.name as nodeName, P.name as policyName
from sto_ov_managednode MN
left join OV_PM_PolicyOnNode PON on pon.nodeid = mn.name
left join OV_PM_PolicyVersion PV on PV.policyversionid = pon.policyversionid
left join OV_PM_Policy P on P.PolicyId = pv.policyid
left join OV_PM_PolicyGroupAssignment PGA on P.PolicyId = PGA.PolicyId
where PGA.GroupId = 'Base Windows'
order by nodeName, policyName;
The top part of the query gets all of the policies for the policy group and does a cross join with the nodes to list one policy for each node regardless of whether the policy exists or not. Then I use EXCEPT to remove the existing node policies.
Result:
nodeName policyName
node2 policy C

Insert a row if it doesn't exist via query

I am trying to write a query that will insert a group of people into a table if that person does not exist. For example, I have table full of people and I need to add more people into the database and I don't know if they are already there. I do know that the social security number (ssn) will never be the same for two people. Could a query be used to check if the ssn is in the table and if not insert the person into the table? If the ssn is in the table then go to the next person and check?
I was thinking about using a stored procedure, but I do not have any rights to create a store procedure.
You can insert your data into a table variable or temp table and then INSERT INTO table from temp table where it does not exists in your table.
DECLARE #Inserted AS TABLE
(
NAME VARCHAR(50)
,SSN DECIMAL(10, 0)
)
INSERT INTO #Inserted
( NAME, SSN )
VALUES ( 'Bob', 123456789 )
, ( 'John', 123546789 )
, ( 'James', 123456798 )
INSERT INTO MyTable
SELECT *
FROM #Inserted AS i
LEFT OUTER JOIN MyTable AS m
ON i.SSN = m.SSN
WHERE m.SSN IS NULL
Here are a couple ideas to get you started. I use MERGE a lot because it offers so much control. You could also look into the IN clause as part of a WHERE predicate in the INSERT SELECT statement.
MERGE
DECLARE #PERSONTABLE TABLE (ID INT PRIMARY KEY IDENTITY(1,1), FirstName VARCHAR(max))
INSERT INTO #PERSONTABLE (FirstName) VALUES ('Bill'),('Sally'),('Bob')
DECLARE #NEWPEOPLE TABLE (FirstName VARCHAR(max))
INSERT INTO #NEWPEOPLE (FirstName) VALUES ('Jim'), ('Sally')
--MERGE
MERGE INTO #PERSONTABLE AS T
USING #NEWPEOPLE AS S
ON (T.FirstName = S.FirstName)
WHEN NOT MATCHED BY TARGET THEN
INSERT (FirstName) VALUES (S.FirstName);
SELECT * FROM #PERSONTABLE
EXCEPT
DECLARE #PERSONTABLE TABLE (ID INT PRIMARY KEY IDENTITY(1,1), FirstName VARCHAR(max))
INSERT INTO #PERSONTABLE (FirstName) VALUES ('Bill'),('Sally'),('Bob')
DECLARE #NEWPEOPLE TABLE (FirstName VARCHAR(max))
INSERT INTO #NEWPEOPLE (FirstName) VALUES ('Jim'), ('Sally')
--EXCEPT
INSERT INTO #PERSONTABLE (FirstName)
SELECT FirstName FROM #NEWPEOPLE
EXCEPT
SELECT FirstName FROM #PERSONTABLE
SELECT * FROM #PERSONTABLE
You could do it like this if the new people are in another table. If not, then use Vladimir's solution.
INSERT INTO People(ssn, firstname, lastname)
SELECT ssn, firstname, lastname
FROM newpeople
WHERE ssn not in (select ssn from people )
INSERT INTO People(ssn, firstname, lastname)
SELECT np.ssn, np.firstname, np.lastname
FROM newpeople np
LEFT JOIN People p on np.ssn = p.ssn
WHERE p.ssn IS NULL
Here's another option I use a lot. Normally joins are better than sub-selects... if the joined table value is null you know you don't have a hit in the joined table.

Find and insert dummy rows - possible scenario for OUTER APPLY

This is a mock up of the situation we've got:
IF OBJECT_ID('TEMPDB..#People') IS NOT NULL BEGIN DROP TABLE #People END;
CREATE TABLE #People
(
Name VARCHAR(100),
Category VARCHAR(20),
ID INT
);
INSERT INTO #People
values
('x','Bronze',1),
('y','Bronze',2),
('z','Silver',3),
('j','Gold',4),
('q','Bronze',5),
('x','Silver',1);
IF OBJECT_ID('TEMPDB..#Category') IS NOT NULL BEGIN DROP TABLE #Category END;
CREATE TABLE #Category
(
Category VARCHAR(100)
);
INSERT INTO #Category
values
('Gold'),
('Silver'),
('Bronze');
If a name does not have a Category e.g. x does not have Gold then I'd like a row creating and adding into the table #People with an ID of -1.
Current solution I have is this:
WITH x AS
(
SELECT DISTINCT
x.Name,
s.Category
FROM #People x
CROSS JOIN #Category s
)
INSERT INTO #People
SELECT J.Name,
J.Category,
ID = -1
FROM x J
WHERE NOT EXISTS
(
SELECT 1
FROM #People Q
WHERE J.Name = Q.Name
AND J.Category = Q.Category
);
See it works!...:
SELECT *
FROM #People;
I have a feeling CROSS APPLY might be a good operator to use in order to simplify the above - What is the simplest way to find, create and insert these rows?
insert into People(Name, Category, Id)
(
select distinct
p.Name,
c.Category,
p.Id
from
people p
cross join category c
where
Concat(p.id, c.Category) not in (select Concat(id, Category) from people)
);
http://www.sqlfiddle.com/#!6/92cd5/14