Convert Table to Specific Column Wise - sql

I have a table like this. How can I convert to this format?
DECLARE #A TaBLE (KeyValue INT, Name VARCHAR(50), Value VARCHAR(512))
INSERT INTO #A
VALUES (0, 'AccountID', '192507'), (0, 'member_id', '999159'),
(0, 'firstname', 'Test1'), (0, 'lastname', 'Last1'),
(1, 'AccountID', '192508'), (1, 'member_id', '999160'),
(1, 'firstname', 'Test2'), (1, 'lastname', 'Last2')
SELECT * FROM #A
I have table rows for this model:
KeyValue Name Value
-----------------------------------
0 AccountID 192507
0 member_id 999159
0 firstname Test1
0 lastname Last1
1 AccountID 192508
1 member_id 999160
1 firstname Test2
1 lastname Last2
My expected output is:
AccountID member_id firstname lastname
--------------------------------------------
192507 999159 Test1 Last1
192508 999160 Test2 Last2
I tried this code But it didn't work
select *
from
(
select Name,value
from #A
) d
pivot
(
MAX(value)
for Name in (AccountID,member_id,firstname,lastname)
) piv;

Try this below logic-
DEMO HERE
SELECT
MAX(CASE WHEN Name = 'AccountID' THEN Value ELSE NULL END) AccountID,
MAX(CASE WHEN Name = 'member_id' THEN Value ELSE NULL END) member_id ,
MAX(CASE WHEN Name = 'firstname' THEN Value ELSE NULL END) firstname ,
MAX(CASE WHEN Name = 'lastname' THEN Value ELSE NULL END) lastname
FROM #A
GROUP BY KeyValue

You can get the desired result by using PIVOT. In your query you just need to select all the columns, like below.
SELECT AccountID, member_id, firstname, lastname
FROM
(
select * from #A
) d
PIVOT
(
MAX(value)
FOR Name IN (AccountID, member_id, firstname, lastname)
) piv;
You can run the test here.

In the temp table, you should select all useful information like this
select AccountID, member_id, firstname, lastname
from
(
select * from #A -- instead of `select Name,value`
) d
pivot
(
MAX(value)
for Name in (AccountID,member_id,firstname,lastname)
) piv;
Result here

Related

SQL to fetch out dump data

I kind of stuck in fetching out the count of unique customers I have in sqlserver table. The way table storing data is:
+----------+----------+----------+
| Value | Label | ClientID |
+----------+----------+----------+
| Mr | Title | 1 |
| Sul | Forename | 1 |
| Last | Surname | 1 |
| WD17 6JJ | Postcode | 1 |
+----------+----------+----------+
Now I have to count\list unique customer on the basis of forename,surname,postcode. Can someone please help
Here are two queries that will give you the desired results:
DECLARE #T table (Value varchar(255), Label varchar(255), ClientID int)
INSERT INTO #T
VALUES
('Mr', 'Title', 1)
, ('Sul', 'Forename', 1)
, ('Last', 'Surname', 1)
, ('WD17 6JJ', 'Postcode', 1)
, ('Dr', 'Title', 2) -- different Title will be ignored
, ('Sul', 'Forename', 2)
, ('Last', 'Surname', 2)
, ('WD17 6JJ', 'Postcode', 2)
, ('Mr', 'Title', 3)
, ('Sul2', 'Forename', 3) -- different Forename
, ('Last', 'Surname', 3)
, ('WD17 6JJ', 'Postcode', 3)
-- Using JOIN
SELECT DISTINCT
T1.Value Forename
, T2.Value Surname
, T3.Value Postcode
FROM
#T T1
JOIN #T T2 ON T1.ClientID = T2.ClientID AND T2.Label = 'Surname'
JOIN #T T3 ON T1.ClientID = T3.ClientID AND T3.Label = 'Postcode'
WHERE T1.Label = 'Forename'
-- Using PIVOT
SELECT DISTINCT
Forename
, Surname
, Postcode
FROM
(
SELECT
Value
, Label
, ClientID
FROM #T
) T
PIVOT
(
MAX (Value)
FOR Label IN
(
Forename, Surname, Postcode
)
) P
for a count of distinct Customers
SELECT COUNT (DISTINCT ClientId)
FROM dbo.table
Or use pivot to get a distinct list
DECLARE #Table TABLE ( [Value] NVARCHAR(20), Label NVARCHAR(20), ClientID INT)
INSERT INTO #Table
([Value], Label, ClientID)
VALUES
(N'Mr', N'Title', 1),
(N'Sul', N'Forename', 1),
(N'Last', N'Surname', 1),
(N'WD17 6JJ', N'Postcode', 1)
SELECT Pvt.Forename
, Pvt.Surname
, Pvt.Postcode
FROM #Table T
PIVOT ( MAX([Value]) FOR Label IN ([Title], [Forename], [Surname], [Postcode])) AS Pvt
GROUP BY
Pvt.Forename
,Pvt.Surname
,Pvt.Postcode
You can do something like this:
SELECT DISTINCT
id.clientID,
title.title,
Forename.Forename,
Surname.Surname,
Postcode.Postcode
FROM table id
CROSS APPLY (SELECT value AS title FROM table title WHERE label = 'title' AND title.clientID = id.clientID ) title
CROSS APPLY (SELECT value AS Forename FROM table Forename WHERE label = 'Forename' AND Forename.clientID = id.clientID ) Forename
CROSS APPLY (SELECT value AS Surname FROM table Surname WHERE label = 'Surname' AND Surname.clientID = id.clientID ) Surname
CROSS APPLY (SELECT value AS Postcode FROM table Postcode WHERE label = 'Postcode' AND Postcode.clientID = id.clientID ) Postcode
It's not pretty, but should work.

How to convert many rows into Columns in SQL Server?

How would you convert a field that is stored as multiple rows into columns?
I listed the code below as well. Below is an example of what is needed but it can really go up to 20 columns. Thanks!
COL1 COL2 COL3
----------------
TEST 30 NY
TEST 30 CA
TEST2 10 TN
TEST2 10 TX
I would like the output to be :
COL1 COL2 COL3 COL4
------------------------
TEST 30 NY CA
TEST2 10 TN TX
select * from (
select
ID,
Name,
STORE,
Group,
Type,
Date,
State,
row_number() over(partition by ID, state order by Date desc) as rn
from
#test
) t
where t.rn = 1
declare #Table AS TABLE
(
Col1 VARCHAR(100) ,
Col2 INT ,
Col3 VARCHAR(100)
)
INSERT #Table
( Col1, Col2, Col3 )
VALUES
( 'TEST', 30 ,'NY' ),
( 'TEST', 30 ,'CA' ),
( 'TEST2', 10 ,'TN' ),
( 'TEST2', 10 ,'TX' )
SELECT
xQ.Col1,
xQ.Col2,
MAX(CASE WHEN xQ.RowNumber = 1 THEN xQ.Col3 ELSE NULL END) AS Col3,
MAX(CASE WHEN xQ.RowNumber = 2 THEN xQ.Col3 ELSE NULL END) AS Col4
FROM
(
SELECT * , RANK() OVER(PARTITION BY T.Col1,T.Col2 ORDER BY T.Col1,T.Col2,T.Col3) AS RowNumber
FROM #Table AS T
)AS xQ
GROUP BY
xQ.Col1,
xQ.Col2
There are multiple options to convert data from rows into columns. In SQL, you can use PIVOT to transform data from rows into columns.
CREATE table #tablename
(Id int, Value varchar(10), ColumnName varchar(15);
INSERT INTO #tablename
(ID, Value, ColumnName)
VALUES
(1, ‘Lucy’, 'FirstName'),
(2, ‘James’, ‘LastName’),
(3, ‘ABCDXX’, ‘Adress’),
(4, ’New York’, ‘City’),
(5, '8572685', ‘PhoneNo’);
select FirstName, LastName, Address, City, PhoneNo
from
(
select Value, ColumnName
from #tablename
) d
pivot
(
max(Value)
for ColumnName in (FirstName, LastName, Address, City, PhoneNo)
) piv;
Refer the below link for other options of transforming data from rows to columns:
https://www.sqlshack.com/multiple-options-to-transposing-rows-into-columns/

Aggregate Function on multiple columns in SQL Server

I have the following data in a #temp table:
Id code Fname CompanyId FieldName Value
----------------------------------------------------------------
465 00133 JENN WILSON 1 ERA 1573
465 00133 JENN WILSON 1 ESHIFTALLOW 3658
465 00133 JENN WILSON 1 NETPAY 51560
I want to do following operation i.e
One Row will be addition on two columns i.e ERA + ESHIFTALLOW
Other Row will be subtraction & addition on three columns i.e NETPAY - ERA + ESHIFTALLOW
I had tried using case statement in SQL Server.
Following is the output required
where Field1= ERA + ESHIFTALLOW & Filed2=NETPAY - ERA + ESHIFTALLOW
Id code Fname CompanyId FieldName Value
----------------------------------------------------------------
465 00133 JENN WILSON 1 Field1 5231
465 00133 JENN WILSON 1 Filed2 46329
I had tried using SQL SERVER Case Statement but not getting proper output
SQL Query : Aggregate option in SQL Server CASE statement
I see at least 2 methods to get those results. A group by or a pivot
In the example below the 2 methods are shown.
CREATE TABLE #Temp (Id INT, code VARCHAR(5), Fname VARCHAR(20), CompanyId INT, FieldName VARCHAR(20), Value INT);
insert into #Temp (Id, code, Fname, CompanyId, FieldName, Value)
values
(465,00133,'JENN WILSON',1,'ERA',1573),
(465,00133,'JENN WILSON',1,'ESHIFTALLOW',3658),
(465,00133,'JENN WILSON',1,'NETPAY',51560);
with Q AS (
SELECT Id, code, Fname, CompanyId,
sum(case when FieldName = 'ERA' then Value end) as ERA,
sum(case when FieldName = 'ESHIFTALLOW' then Value end) as ESHIFTALLOW,
sum(case when FieldName = 'NETPAY' then Value end) as NETPAY
from #Temp
group by Id, code, Fname, CompanyId
)
select Id, code, Fname, CompanyId, 'Field1' as FieldName, (ERA + ESHIFTALLOW) as Value from Q
union all
select Id, code, Fname, CompanyId, 'Field2', (NETPAY - ERA + ESHIFTALLOW) from Q
;
with Q AS (
SELECT Id, code, Fname, CompanyId,
(ERA + ESHIFTALLOW) as Field1,
(NETPAY - ERA + ESHIFTALLOW) as Field2
FROM (SELECT * FROM #Temp) s
PIVOT ( SUM(VALUE) FOR FieldName IN (ERA, ESHIFTALLOW, NETPAY)) p
)
select Id, code, Fname, CompanyId, 'Field1' as FieldName, Field1 as Value from Q
union all
select Id, code, Fname, CompanyId, 'Field2', Field2 from Q
;
Note that SUM(VALUE) was used instead of MAX(VALUE). In this case it will yield the same results. It's just a choice really.
Building heavily on LukStorms' answer, you can use a PIVOT and an UNPIVOT to get the results you want:
CREATE TABLE #Temp
(Id INT, Code VARCHAR(5), Fname VARCHAR(20), CompanyId INT, FieldName VARCHAR(20), Value INT);
INSERT INTO #Temp
(Id, Code, Fname, CompanyId, FieldName, Value)
VALUES
(465,00133, 'JENN WILSON', 1, 'ERA', 1573),
(465,00133, 'JENN WILSON', 1, 'ESHIFTALLOW', 3658),
(465,00133, 'JENN WILSON', 1, 'NETPAY', 51560);
SELECT Id, Code, Fname, CompanyId, FieldName, Value
FROM (
SELECT Id, Code, Fname, CompanyId,
ERA + ESHIFTALLOW AS Field1,
NETPAY - ERA + ESHIFTALLOW AS Field2
FROM (
SELECT *
FROM #Temp
) AS s
PIVOT (
SUM(Value)
FOR FieldName IN (ERA, ESHIFTALLOW, NETPAY)
) AS p
) AS r
UNPIVOT (
Value
FOR FieldName IN (Field1, Field2)
) AS u
;
I have no idea whether this solution is anywhere near the most efficient, but it should work:
SELECT
BASE.*,
ERA.Value AS ERA,
ESALLOW.Value AS ESHIFTALLOW,
ERA.Value + ESALLOW.Value AS Field1,
etc...
FROM (
SELECT DISTINCT Id, code, Fname, CompanyId
FROM #TEMP ) BASE
LEFT OUTER JOIN (
SELECT Id, Value
FROM #TEMP
WHERE FieldName = 'ERA' ) ERA
ON BASE.Id = ERA.Id
LEFT OUTER JOIN (
SELECT Id, Value
FROM #TEMP
WHERE FieldName = 'ESHIFTALLOW' ) ESALLOW
ON BASE.Id = ESALLOW.Id
This gives you a simple table that has every type of value in a separate column, instead of in separate rows. This makes calculations possible.

Remove duplicates with less null values

I have a table of employees which contains about 25 columns. Right now there are a lot of duplicates and I would like to try and get rid of some of these duplicates.
First, I want to find the duplicates by looking for multiple records that have the same values in first name, last name, employee number, company number and status.
SELECT
firstname,lastname,employeenumber, companynumber, statusflag
FROM
employeemaster
GROUP BY
firstname,lastname,employeenumber,companynumber, statusflag
HAVING
(COUNT(*) > 1)
This gives me duplicates but my goal is to find and keep the best single record and delete the other records. The "best single record" is defined by the record with the least amount of NULL values in all of the other columns. How can I do this?
I am using Microsoft SQL Server 2012 MGMT Studio.
EXAMPLE:
Red: DELETE
Green: KEEP
NOTE: There are a lot more columns in the table than what this table shows.
You can use the sys.columns table to get a list of columns and build a dynamic query. This query will return a 'KeepThese' value for every record you want to keep based on your given criteria.
-- insert test data
create table EmployeeMaster
(
Record int identity(1,1),
FirstName varchar(50),
LastName varchar(50),
EmployeeNumber int,
CompanyNumber int,
StatusFlag int,
UserName varchar(50),
Branch varchar(50)
);
insert into EmployeeMaster
(
FirstName,
LastName,
EmployeeNumber,
CompanyNumber,
StatusFlag,
UserName,
Branch
)
values
('Jake','Jones',1234,1,1,'JJONES','PHX'),
('Jake','Jones',1234,1,1,NULL,'PHX'),
('Jake','Jones',1234,1,1,NULL,NULL),
('Jane','Jones',5678,1,1,'JJONES2',NULL);
-- get records with most non-null values with dynamic sys.column query
declare #sql varchar(max)
select #sql = '
select e.*,
row_number() over(partition by
e.FirstName,
e.LastName,
e.EmployeeNumber,
e.CompanyNumber,
e.StatusFlag
order by n.NonNullCnt desc) as KeepThese
from EmployeeMaster e
cross apply (select count(n.value) as NonNullCnt from (select ' +
replace((
select 'cast(' + c.name + ' as varchar(50)) as value union all select '
from sys.columns c
where c.object_id = t.object_id
for xml path('')
) + '#',' union all select #','') + ')n)n'
from sys.tables t
where t.name = 'EmployeeMaster'
exec(#sql)
Try this.
;WITH cte
AS (SELECT Row_number()
OVER(
partition BY firstname, lastname, employeenumber, companynumber, statusflag
ORDER BY (SELECT NULL)) rn,
firstname,
lastname,
employeenumber,
companynumber,
statusflag,
username,
branch
FROM employeemaster),
cte1
AS (SELECT a.firstname,
a.lastname,
a.employeenumber,
a.companynumber,
a.statusflag,
Row_number()
OVER(
partition BY a.firstname, a.lastname, a.employeenumber, a.companynumber, a.statusflag
ORDER BY (CASE WHEN a.username IS NULL THEN 1 ELSE 0 END +CASE WHEN a.branch IS NULL THEN 1 ELSE 0 END) )rn
-- add the remaining columns in case statement
FROM cte a
JOIN employeemaster b
ON a.firstname = b.firstname
AND a.lastname = b.lastname
AND a.employeenumber = b.employeenumber
AND a.companynumbe = b.companynumber
AND a.statusflag = b.statusflag)
SELECT *
FROM cte1
WHERE rn = 1
I test with MySQL and use NULL String concat to found the best record. Because LENGTH ( NULL || 'data') is 0. Only if all column not NULL some length exists. Maybe this is not perfekt.
create table EmployeeMaster
(
Record int auto_increment,
FirstName varchar(50),
LastName varchar(50),
EmployeeNumber int,
CompanyNumber int,
StatusFlag int,
UserName varchar(50),
Branch varchar(50),
PRIMARY KEY(record)
);
INSERT INTO EmployeeMaster
(
FirstName, LastName, EmployeeNumber, CompanyNumber, StatusFlag, UserName, Branch
) VALUES ('Jake', 'Jones', 1234, 1, 1, 'JJONES', 'PHX'), ('Jake', 'Jones', 1234, 1, 1, NULL, 'PHX'), ('Jake', 'Jones', 1234, 1, 1, NULL, NULL), ('Jane', 'Jones', 5678, 1, 1, 'JJONES2', NULL);
My query idea looks like this
SELECT e.*
FROM employeemaster e
JOIN ( SELECT firstname,
lastname,
employeenumber,
companynumber,
statusflag,
MAX( LENGTH ( username || branch ) ) data_quality
FROM employeemaster
GROUP BY firstname, lastname, employeenumber, companynumber, statusflag
HAVING count(*) > 1
) g
ON LENGTH ( username || branch ) = g.data_quality

Transform SQL Table

We have a table that has the following format
RecordID FieldName FieldValue
1 Name John
1 Age 30
2 Name Alice
2 Age 40
We would like to present this as:
John 30
Alice 40
Anyone have a good solution for this?
SELECT
TN.FieldValue AS ValueName
, TV.FieldValue AS ValueAge
FROM dbo.Table1 TN
INNER JOIN dbo.Table1 TV ON TN.RecordID = TV.RecordID
AND TN.FieldName = 'Name'
AND TV.FieldName = 'Age'
Why didn't anybody use pivot?
declare #a TABLE(RecordId int, FieldName varchar(20), FieldValue varchar(20))
insert into #a select 1, 'Name', 'John'
insert into #a select 1, 'Age', '30'
insert into #a select 2, 'Name', 'Alice'
insert into #a select 2, 'Age', '40'
select *
from #a a pivot (max(FieldValue) for FieldName in (Name,Age)) p
declare #a TABLE(RecordId int, FieldName varchar(20), FieldValue varchar(20))
insert into #a select 1, 'Name', 'John'
insert into #a select 1, 'Age', '30'
insert into #a select 2, 'Name', 'Alice'
insert into #a select 2, 'Age', '40'
select
records.RecordId
, name.fieldvalue [Name]
, age.fieldvalue [Age]
from
(select distinct recordid from #a) records
inner join #a name on records.recordid = name.recordid and name.fieldname = 'Name'
inner join #a age on records.recordid = age.recordid and age.fieldname = 'Age'
This ain't pretty (but nor's the data), but this will work if your column names are known when you write the query:
SELECT
RecordId,
group_concat( if( FieldName = 'name', FieldValue, '' ) SEPARATOR '' ) AS person_name,
group_concat( if( FieldName = 'age', FieldValue, '' ) SEPARATOR '' ) AS person_age
FROM test_table
GROUP BY RecordId
(works in MySQL 5.0)
You should also use CAST to get the fields to the correct type
If you don't know the fields, you'll need to (programatically) do a select distinct FieldName beforehand in order to build your query
I've a feeling the the Drupal Content Creation Kit (CCK) does something rather similar to this