Query to split data from rows into columns - sql

I have a table that looks like this in SQL Server:
Client Name
Client ID Type
Client ID
John Dee
Netflix ID
2563
John Dee
Facebook ID
546543
Sara Spence
Netflix ID
7985
Sara Spence
Facebook ID
568867
Mark Brown
Netflix ID
8635
Mark Brown
Facebook ID
597489
I want to make query and show the data from row into column, expecting the result like this:
Client Name
Netflix ID
Facebook ID
John Dee
2563
546543
Sara Spence
7985
568867
Mark Brown
8635
597489
Is there any solution to show the table from row into column? I have many data in the table.
Version
Microsoft SQL Server 2019 (RTM-CU15) (KB5008996) - 15.0.4198.2 (X64) Jan 12 2022 22:30:08 Copyright (C) 2019 Microsoft Corporation
Enterprise Edition (64-bit) on Windows Server 2016 Datacenter 10.0 (Build 14393: ) (Hypervisor)

Please try the following solution.
It follows the same minimal reproducible example paradigm. It is copied to SSMS as-is, executed, and you are getting the desired output.
SQL
-- DDL and sample data population, start
DECLARE #tbl TABLE (ClientName VARCHAR(20), ClientIDType VARCHAR(20), ClientID INT);
INSERT #tbl (ClientName, ClientIDType, ClientID) VALUES
('John Dee', 'Netflix ID', 2563),
('John Dee', 'Facebook ID', 546543),
('Sara Spence', 'Netflix ID', 7985),
('Sara Spence', 'Facebook ID', 568867),
('Mark Brown', 'Netflix ID', 8635),
('Mark Brown', 'Facebook ID', 597489);
-- DDL and sample data population, end
SELECT ClientName, [Netflix ID] AS NetflixID, [Facebook ID] AS FacebookID
FROM
( SELECT ClientName, ClientIDType, ClientID
FROM #tbl
) ps
PIVOT
( SUM (ClientID)
FOR ClientIDType IN ( [Netflix ID], [Facebook ID])
) AS pvt;
Output
ClientName
NetflixID
FacebookID
John Dee
2563
546543
Mark Brown
8635
597489
Sara Spence
7985
568867

SQL Server provides a PIVOT and UNPIVOT feature that should do what you are looking for.
https://learn.microsoft.com/en-us/sql/t-sql/queries/from-using-pivot-and-unpivot?view=sql-server-ver15

Just a JOIN would do in this case:
SELECT
t1.ClientName,
t1.ClientID as NetflixID,
t2.ClientID as FacebookID
FROM tbl t1
INNER JOIN tbl t2 on t1.ClientName=t2.ClientName and t2.ClientIDType='Facebook ID'
WHERE t1.ClientIdType='Netflix ID'
see: DBFIDDLE
or one could just group by ClientName:
select
ClientName,
SUM(CASE WHEN ClientIDType='Netflix ID' THEN ClientID END) as 'Netflix ID',
SUM(CASE WHEN ClientIDType='Facebook ID' THEN ClientID END) as 'Facebook ID'
FROM tbl
GROUP BY ClientName;

Related

SQL View Assign Alternating Data from Another Table

I cannot explain this thoroughly with words as English is not my native language so I will try visual presentation. First, I have a table that looks like this let's call it tblPerson:
tblPerson
ID Name
1 John
2 Paul
3 James
Then I have another table tblPhones:
tblPhones
ID Mobile
1 123456
2 654321
Now for my question, is it possible to create a view that will look like this:
Person-Phone
Name Mobile
John 123456
Paul 654321
James 123456
What I want to display is a list of person, and use the tblPhones to assign the mobile column but assign it alternately. So if a new person is added lets say Mark. The view would look like this:
Person-Phone
Name Mobile
John 123456
Paul 654321
James 123456
Mark 654321
How can I query this?
Try this:
SELECT Name, Mobile
FROM (
SELECT Name,
ROW_NUMBER() OVER (ORDER BY ID) As rn
FROM tblPerson) AS t1
INNER JOIN (
SELECT Mobile,
ROW_NUMBER() OVER (ORDER BY ID) AS rn,
COUNT(*) OVER () AS cnt
FROM tblPhones
) AS t2 ON (t1.rn - 1) % cnt + 1 = t2.rn
Demo here
#Giorgos beat me to it, but here's my version. You don't need the row_number window function assuming the IDs are contiguous (if they're not, you do :).
CREATE TABLE #tblPerson (ID INT,Name VARCHAR(5));
CREATE TABLE #tblPhones (ID INT, Mobile VARCHAR(6));
INSERT INTO #tblPerson(ID, Name) VALUES( 1, 'John'),( 2, 'Paul'),( 3, 'James');
INSERT INTO #tblPhones(ID, Mobile) VALUES( 1,'123456'),( 2,'654321');
SELECT
Name, Mobile
FROM #tblPerson
JOIN #tblPhones ON #tblPhones.ID = ((#tblPerson.ID-1) % (SELECT COUNT(*) FROM #tblPhones) +1)
ORDER BY #tblPerson.ID

SQL Server : query to update record with latest entry

I have a table that maintains records of employers and employees' data. Something like this
EmployerName EmployerPhone EmployerAddress EmployeeName EmployeePhone EmployeeAddress Date
-------------------------------------------------------------------------------------------------------
John 12345 NewYork Harry 59786 NewYork 12-1-1991
Mac 22345 Bankok John 12345 Delhi 12-3-1991
Smith 54732 Arab Amar 59226 China 21-6-1991
Sarah 12345 Bhutan Mac 22345 NewYork 5-9-1991
Root 85674 NewYork Smith 54732 Japan 2-11-1991
I have another table that will have generic records on the basis of phone number (both employers and employees).
Table structure is as following
Phone Name Address
I want to put latest records according to date from Table1 to Table2 on the basis of phone..
Like this
Phone Name Address
-----------------------
59786 Harry NewYork
22345 Mac NewYork
59226 Amar China
12345 Sarah Bhutan
22345 Mac NewYork
85674 Root NewYork
54732 Smith Arab
I've written many queries but couldn't find anyone resulted as required.
Any kind of help will be appreciated.
For initialize the table without phone duplicates:
INSERT IGNORE INTO Table2 (Phone, Name, Address)
SELECT X.* FROM (
SELECT EmployeeName,EmployeePhone,EmployeeAddress FROM Table1
UNION
SELECT EmployerName,EmployerPhone,EmployerAddress FROM Table1
) X
WHERE NOT EXISTS (SELECT Phone FROM Table2 WHERE Phone=X.Phone)
I think this is what you are looking for if I understand your question correctly. Should work for a once-off
DECLARE #restbl TABLE
(
Name varchar(100),
Phone varchar(20),
Addr varchar(100),
[Date] date,
RecType varchar(100)
)
INSERT INTO #restbl
SELECT EmployerName, EmployerPhone, NULL, MAX([Date]), 'Employer'
FROM #tbl
GROUP BY EmployerName, EmployerPhone
INSERT INTO #restbl
SELECT EmployeeName, EmployeePhone, NULL, MAX([Date]), 'Employee'
FROM #tbl
GROUP BY EmployeeName, EmployeePhone;
WITH LatestData (Name, Phone, [Date])
AS
(
SELECT Name, Phone, MAX([Date])
FROM #restbl
GROUP BY Name, Phone
)
INSERT INTO FinalTable (Name, Phone, [Address])
SELECT DISTINCT ld.Name, ld.Phone, ISNULL(tEmployer.EmployerAddress, tEmployee.EmployeeAddress) AS [Address]
FROM LatestData ld
LEFT JOIN #tbl tEmployer ON ld.Name = tEmployer.EmployerName AND ld.Phone = tEmployer.EmployerPhone AND ld.Date = tEmployer.Date
LEFT JOIN #tbl tEmployee ON ld.Name = tEmployee.EmployeeName AND ld.Phone = tEmployee.EmployeePhone AND ld.Date = tEmployee.Date

ms-access 2010: count duplicate names per household address

I am currently working with a spreadsheet in MS Access 2010 which contains about 130k rows of information about people who voted in a local election recently. Each row has their residential information (street name, number, postcode etc.) and personal information (title, surname, forename, middle name, DOB etc.). Each row represents an individual person rather than a household (therefore in many cases the same residential address appears more than once as more than one person resides in a particular household).
What I want to achieve is basically to create a new field in this dataset called 'count'. I want this field to give me a count of how many different surnames reside at a single address.
Is there an SQL script that will allow me to do this in Access 2010?
+------------------+----------+-------+---------+----------+-------------+
| PROPERTYADDRESS1 | POSTCODE | TITLE | SURNAME | FORENAME | MIDDLE_NAME |
+------------------+----------+-------+---------+----------+-------------+
FAKEADDRESS1 EEE 5GG MR BLOGGS JOE N
FAKEADDRESS2 EEE 5BB MRS BLOGGS SUZANNE P
FAKEADDRESS3 EEE 5RG MS SMITH PAULINE S
FAKEADDRESS4 EEE 4BV DR JONES ANNE D
FAKEADDRESS5 EEE 3AS MR TAYLOR STUART A
The following syntax has got me close so far:
SELECT COUNT(electoral.SURNAME)
FROM electoral
GROUP BY electoral.UPRN
However, instead of returning me all 130k odd rows, it only returns me around 67k rows. Is there anything I can do to the syntax to achieve the same result, but just returning every single row?
Any help is greatly appreciated!
Thanks
You could use something like this:
select *,
count(surname) over (partition by householdName)
from myTable
If you have only one column which contains the name,
ex: Rob Adams
then you can do this to have all the surnames in a different column so it will be easier in the select:
SELECT LEFT('HELLO WORLD',CHARINDEX(' ','HELLO WORLD')-1)
in our example:
select right (surmane, charindex (' ',surname)-1) as surname
example on how to use charindex, left and right here:
http://social.technet.microsoft.com/wiki/contents/articles/17948.t-sql-right-left-substring-and-charindex-functions.aspx
if there are any questions, leave a comment.
EDIT: I edited the query, had a syntax error, please try it again. This works on sql server.
here is an example:
create table #temp (id int, PropertyAddress varchar(50), surname varchar(50), forname varchar(50))
insert into #temp values
(1, 'hiddenBase', 'Adamns' , 'Kara' ),
(2, 'hiddenBase', 'Adamns' , 'Anne' ),
(3, 'hiddenBase', 'Adamns' , 'John' ),
(4, 'QueensResidence', 'Queen' , 'Oliver' ),
(5, 'QueensResidence', 'Queen' , 'Moira' ),
(6, 'superSecretBase', 'Diggle' , 'John' ),
(7, 'NandaParbat', 'Merlin' , 'Malcom' )
select * from #temp
select *,
count (surname) over (partition by PropertyAddress) as CountMembers
from #temp
gives:
1 hiddenBase Adamns Kara 3
2 hiddenBase Adamns Anne 3
3 hiddenBase Adamns John 3
7 NandaParbat Merlin Malcom 1
4 QueensResidence Queen Oliver 2
5 QueensResidence Queen Moira 2
6 superSecretBase Diggle John 1
Your query should look like this:
select *,
count (SURNAME) over (partition by PropertyAddress) as CountFamilyMembers
from electoral
EDIT
If over partition by isn't supported, then I guess you can get to your desired result by using group by
select *,
count (SURNAME) over (partition by PropertyAddress) as CountFamilyMembers
from electoral
group by -- put here the fields in the select (one by one), however you can't write group by *
GROUP BY creates an aggregate query, so it's by design that you get fewer records (one per UPRN).
To get the count for each row in the original table, you can join the table with the aggregate query:
SELECT electoral.*, elCount.NumberOfPeople
FROM electoral
INNER JOIN
(
SELECT UPRN, COUNT(*) AS NumberOfPeople
FROM electoral
GROUP BY UPRN
) AS elCount
ON electoral.UPRN = elCount.UPRN
Given the update I want to post another answer. Try it like this:
create table #temp2 ( PropertyAddress1 varchar(50), POSTCODE varchar(20), TITLE varchar (20),
surname varchar(50), FORENAME varchar(50), MIDDLE_NAME varchar (50) )
insert into #temp2 values
('FAKEADDRESS1', 'EEE 5GG', 'MR', 'BLOGGS', 'JOE', 'N'),
('FAKEADDRESS1', 'EEE 5BB', 'MRS', 'BLOGGS', 'SUZANNE', 'P'),
('FAKEADDRESS2', 'EEE 5RG', 'MS', 'SMITH', 'PAULINE', 'S'),
('FAKEADDRESS3', 'EEE 4BV', 'DR', 'JONES', 'ANNE', 'D'),
('FAKEADDRESS4', 'EEE 3AS', 'MR', 'TAYLOR', 'STUART', 'A')
select PropertyAddress1, surname,count (#temp2.surname) as CountADD
into #countTemp
from #temp2
group by PropertyAddress1, surname
select * from #temp2 t2
left join #countTemp ct
on t2.PropertyAddress1 = ct.PropertyAddress1 and t2.surname = ct.surname
This yields:
PropertyAddress1 POSTCODE TITLE surname FORENAME MIDDLE_NAME PropertyAddress1 surname CountADD
FAKEADDRESS1 EEE 5GG MR BLOGGS JOE N FAKEADDRESS1 BLOGGS 2
FAKEADDRESS1 EEE 5BB MRS BLOGGS SUZANNE P FAKEADDRESS1 BLOGGS 2
FAKEADDRESS2 EEE 5RG MS SMITH PAULINE S FAKEADDRESS2 SMITH 1
FAKEADDRESS3 EEE 4BV DR JONES ANNE D FAKEADDRESS3 JONES 1
FAKEADDRESS4 EEE 3AS MR TAYLOR STUART A FAKEADDRESS4 TAYLOR 1

Query to exclude two or more records if they match a single value

I have a database table in which multiple customers can be assigned to multiple types. I am having trouble formulating a query that will exclude all customer records that match a certain type. For example:
ID CustomerName Type
=========================
111 John Smith TFS-A
111 John Smith PRO
111 John Smith RWAY
222 Jane Doe PRO
222 Jane Doe TFS-A
333 Richard Smalls PRO
444 Bob Rhoads PRO
555 Jacob Jones TFS-B
555 Jacob Jones TFS-A
What I want is to pull only those people who are marked PRO but not marked TFS. If they are PRO and TFS, exclude them.
Any help is greatly appreciated.
You can get all 'PRO' customers and use NOT EXISTS clause to exclude the ones that are also 'TFS':
SELECT DISTINCT ID, CustomerName
FROM mytable AS t1
WHERE [Type] = 'PRO' AND NOT EXISTS (SELECT 1
FROM mytable AS t2
WHERE t1.ID = t2.ID AND [Type] LIKE 'TFS%')
SQL Fiddle Demo
solution using EXCEPT
WITH TestData
AS (
SELECT *
FROM (
VALUES ( 111, 'John Smith', 'TFS-A' )
, ( 111, 'John Smith', 'PRO' )
, ( 111, 'John Smith', 'RWAY' )
, ( 222, 'Jane Doe', 'PRO' )
, ( 222, 'Jane Doe', 'TFS-A' )
, ( 333, 'Richard Smalls', 'PRO' )
, ( 444, 'Bob Rhoads', 'PRO' )
, ( 555, 'Jacob Jones', 'TFS-B' )
, ( 555, 'Jacob Jones', 'TFS-A' ))
AS t (ID, CustomerName, [Type])
)
SELECT ID, CustomerName
FROM TestData
WHERE [Type] = 'PRO'
EXCEPT
SELECT ID, CustomerName
FROM TestData
WHERE [Type] LIKE 'TFS%'
output result
Select DISTINCT(Customername),ID
FROM tablename
WHERE NOT (ID IN (SELECT ID FROM tablename WHERE type='PRO')
AND ID IN (SELECT ID FROM tablename WHERE type='TFS'))
EDIT: now added working TFS clause
Get all customers that do not have TYPE PRO AND TFS for example
SQLFIDDLE:http://sqlfiddle.com/#!9/da4f9/2
Try This :
SELECT *
FROM table a
WHERE Type = 'PRO'
AND NOT EXISTS(SELECT 1
FROM table b
WHERE a.ID = b.ID
AND LEFT(Type, 3) = 'TFS')
I know this question has been answered, but mine answer is different. Everyone else solutions involves two queries which means what I call "double-dipping". You have to look access the same table twice. It's better to avoid this when possible for better performance. Check this out:
SELECT ID,
CustomerName,
MIN([type]) AS [Type] --doesn't matter if it's MIN or MAX
FROM yourTable
WHERE [Type] = 'PRO' --only load values that matter. Ignore RWAY
OR [Type] LIKE 'TFS-_' --notice I use a "_" instead of "%". That because "_" is a wildcard for a single character
--instead of wildcard looking for any number of characters because normally it's best to be as narrow as possible to be more efficient
GROUP BY ID,CustomerName
HAVING SUM(CASE
WHEN [Type] = 'Pro' --This is where it returns values that only have type PRO
THEN 9999
ELSE 1
END
) = 9999
So let me explain my funky HAVING logic. So as you can see it's a SUM() so and for PRO it's 9999 and TFS-_ it's 1. So when the sum is EXACTLY 9999, then it's good. Why I can't just do a COUNT(*) = 1 is because if a value has only one TFS and no pro, it would be returned, which of course would be incorrect.
Results:
ID CustomerName Type
----------- -------------- -----
444 Bob Rhoads PRO
333 Richard Smalls PRO

sql recursive function - to find managers

Lets say I have the following table
User_ID Manager_ID
---------------------
Linda Jacob
Mark Linda
Kevin Linda
Steve Mark
John Kevin
Basically the requirement is to pull all the managers under the user_id you are searching for. So for instance if I send in 'Linda' then it should return me:
'Mark', 'Kevin', 'Steve', 'John'
or if I send in 'Mark' then it should return me:
Steve
I have heard of recursive function but I am unsure of how to do this. Any help would be appreciated.
Use:
WITH hieararchy AS (
SELECT t.user_id
FROM YOUR_TABLE t
WHERE t.manager_id = 'Linda'
UNION ALL
SELECT t.user_id
FROM YOUR_TABLE t
JOIN hierarchy h ON h.user_id = t.manager_id)
SELECT x.*
FROM hierarchy x
Resultset:
user_id
--------
Mark
Kevin
John
Steve
Scripts:
CREATE TABLE [dbo].[YOUR_TABLE](
[user_id] [varchar](50) NOT NULL,
[manager_id] [varchar](50) NOT NULL
)
INSERT INTO YOUR_TABLE VALUES ('Linda','Jacob')
INSERT INTO YOUR_TABLE VALUES ('Mark','Linda')
INSERT INTO YOUR_TABLE VALUES ('Kevin','Linda')
INSERT INTO YOUR_TABLE VALUES ('Steve','Mark')
INSERT INTO YOUR_TABLE VALUES ('John','Kevin')
The code example from Recursive Queries Using Common Table Expressions on MSDN shows exactly that.