sql server single row multiple columns into one column - sql

I have table like this
Reg_No Student_Name Subject1 Subject2 Subject3 Subject4 Total
----------- -------------------- ----------- ----------- ----------- ----------- -----------
101 Kevin 85 94 78 90 347
102 Andy 75 88 91 78 332
From this I need to create a temp table or table like this:
Reg_No Student_Name Subject Total
----------- -------------------- ----------- -----------
101 Kevin 85 347
94
78
90
102 Andy 75 332
88
91
78
Is there a way I can do this in SQL Server?

DDL:
DECLARE #temp TABLE
(
Reg_No INT
, Student_Name VARCHAR(20)
, Subject1 INT
, Subject2 INT
, Subject3 INT
, Subject4 INT
, Total INT
)
INSERT INTO #temp (Reg_No, Student_Name, Subject1, Subject2, Subject3, Subject4, Total)
VALUES
(101, 'Kevin', 85, 94, 78, 90, 347),
(102, 'Andy ', 75, 88, 91, 78, 332)
Query #1 - ROW_NUMBER:
SELECT Reg_No = CASE WHEN rn = 1 THEN t.Reg_No END
, Student_Name = CASE WHEN rn = 1 THEN t.Student_Name END
, t.[Subject]
, Total = CASE WHEN rn = 1 THEN t.Total END
FROM (
SELECT
Reg_No
, Student_Name
, [Subject]
, Total
, rn = ROW_NUMBER() OVER (PARTITION BY Reg_No ORDER BY 1/0)
FROM #temp
UNPIVOT
(
[Subject] FOR tt IN (Subject1, Subject2, Subject3, Subject4)
) unpvt
) t
Query #2 - OUTER APPLY:
SELECT t.*
FROM #temp
OUTER APPLY
(
VALUES
(Reg_No, Student_Name, Subject1, Total),
(NULL, NULL, Subject2, NULL),
(NULL, NULL, Subject3, NULL),
(NULL, NULL, Subject4, NULL)
) t(Reg_No, Student_Name, [Subject], Total)
Query Plan:
Query Cost:
Output:
Reg_No Student_Name Subject Total
----------- -------------------- ----------- -----------
101 Kevin 85 347
NULL NULL 94 NULL
NULL NULL 78 NULL
NULL NULL 90 NULL
102 Andy 75 332
NULL NULL 88 NULL
NULL NULL 91 NULL
NULL NULL 78 NULL
PS: In your case query with OUTER APPLY is faster than ROW_NUMBER solution.

The simplest approach would be to use a UNIONclause
select Reg_No, Student_Name, Subject1, Total from YourTable union all
select Reg_No, Student_Name, Subject2, Total from YourTable union all
select Reg_No, Student_Name, Subject3, Total from YourTable union all
select Reg_No, Student_Name, Subject3, Total from YourTable
UNION
Combines the results of two or more queries into a single result set
that includes all the rows that belong to all queries in the union.
The UNION operation is different from using joins that combine columns
from two tables.
The following are basic rules for combining the result sets of two
queries by using UNION:
•The number and the order of the columns must be the same in all
queries.
•The data types must be compatible.

Check this Fiddle
;WITH MyCTE AS
(
SELECT *
FROM (
SELECT Reg_No,
[Subject1],
[Subject2],
[Subject3],
[Subject4]
FROM Table1
)p
UNPIVOT
(
Result FOR SubjectName in ([Subject1], [Subject2], [Subject3], [Subject4])
)unpvt
)
SELECT T.Reg_No,
T.Student_Name,
M.SubjectName,
M.Result,
T.Total
FROM Table1 T
JOIN MyCTE M
ON T.Reg_No = M.Reg_No
If you do want NULL values in the rest, you may try the following:
This is the new Fiddle
And here is the code:
;WITH MyCTE AS
(
SELECT *
FROM (
SELECT Reg_No,
[Subject1],
[Subject2],
[Subject3],
[Subject4]
FROM Table1
)p
UNPIVOT
(
Result FOR SubjectName in ([Subject1], [Subject2], [Subject3], [Subject4])
)unpvt
),
MyNumberedCTE AS
(
SELECT *,
ROW_NUMBER() OVER(PARTITION BY Reg_No ORDER BY Reg_No,SubjectName) AS RowNum
FROM MyCTE
)
SELECT T.Reg_No,
T.Student_Name,
M.SubjectName,
M.Result,
T.Total
FROM MyCTE M
LEFT JOIN MyNumberedCTE N
ON N.Reg_No = M.Reg_No
AND N.SubjectName = M.SubjectName
AND N.RowNum=1
LEFT JOIN Table1 T
ON T.Reg_No = N.Reg_No

You should look after the PIVOT operator :
http://technet.microsoft.com/en-us/library/ms177410(v=sql.100).aspx

> DECLARE #cols AS NVARCHAR(MAX),#query AS NVARCHAR(MAX)
>
> select #cols = STUFF((SELECT ',' + QUOTENAME(designation)
> from MyTable
> group by designation
> order by designation
> FOR XML PATH(''), TYPE
> ).value('.', 'NVARCHAR(MAX)'),1,1,'')
>
> set #query = N'SELECT Row, ' + #cols + N' from
> (
> select ''SS'' Row, SS AS Value , designation from MyTable
> UNION ALL
> select ''AS'' Row, [AS] AS Value , designation from MyTable
> UNION ALL
> select ''Vac'' Row, Vac AS Value , designation from MyTable
> ) x
> pivot
> (
> max(Value) for designation in (' + #cols + N')
> ) p '
> exec sp_executesql #query;
For more details: Convert row into column when number of row is not fixed

Related

Transform rows to columns(probably pivot)

Here is my requirement:
create table #TEMP
(
KEY_VALUE VARCHAR(100)
,NAME VARCHAR(100)
,AMOUNT INT
,QUANTITY INT
)
INSERT INTO #TEMP
VALUES
('K1','ABC',100,10000),
('K2','XYZ',200,20000),
('K1','ABC',50,5000),
('K2','XYZ',300,30000),
('K3','MNO',50,500)
select * from #TEMP
Because the KEY_VALUE COLUMN matches for 2 rows(K1 and K2), I want to transform it to something as below:
KEY_VALUE NAME AMOUNT_1 AMOUNT_2 QUANTITY_1 QUANTITY_2
K1 ABC 100 50 10000 5000
K2 XYZ 200 300 20000 30000
K3 MNO 50 NULL 500 NULL
What/How do I do that? Please let me know if my question is not clear.
You can use ROW_NUMBER() & do conditional aggregation :
SELECT KEY_VALUE, NAME,
MAX(CASE WHEN seq = 1 THEN AMOUNT END) AS AMOUNT_1,
MAX(CASE WHEN seq = 2 THEN AMOUNT END) AS AMOUNT_2,
MAX(CASE WHEN seq = 1 THEN QUANTITY END) AS QUANTITY_1,
MAX(CASE WHEN seq = 2 THEN QUANTITY END) AS QUANTITY_2
FROM (SELECT t.*,
ROW_NUMBER() OVER (PARTITION BY KEY_VALUE ORDER BY AMOUNT) AS seq
FROM #TEMP t
) t
GROUP BY KEY_VALUE, NAME;
EDIT : If you want to do further calculation then you can use CTE :
WITH CTE AS (
<query>
)
SELECT C.*,
C.AMOUNT_1 - C.AMOUNT_1 AS Diff_Amt
FROM CTE C;
Prepare data
CREATE TABLE #t (
key_value varchar(10),
name varchar(10),
amount int,
quantity int
);
INSERT INTO #t
VALUES
('K1', 'ABC', 100, 10000),
('K2', 'XYZ', 200, 20000),
('K1', 'ABC', 50, 5000),
('K2', 'XYZ', 300, 30000),
('K3', 'MNO', 50, 500);
Querying
WITH t1 (id, key_value, name, amount, quantity)
AS (
SELECT ROW_NUMBER() OVER (ORDER BY key_value), key_value, name, amount, quantity FROM #t
),
t2
AS (
SELECT MIN(id) AS min_id, MAX(id) AS max_id, key_value, name
FROM t1
GROUP BY key_value, name
),
t3
AS (
SELECT t2.key_value, t2.name,
t11.amount AS amount_1, t11.quantity AS quantity_1,
t12.amount AS amount_2, t12.quantity AS quantity_2
FROM t2
INNER JOIN
t1 t11 ON t11.key_value = t2.key_value AND t11.name = t2.name
AND t11.id = t2.min_id
LEFT JOIN
t1 t12 ON t12.key_value = t2.key_value AND t12.name = t2.name
AND t12.id = t2.max_id AND t12.id <> t2.min_id
)
SELECT * FROM t3
Result
key_value name amount_1 quantity_1 amount_2 quantity_2
---------- ---------- ----------- ----------- ----------- -----------
K1 ABC 100 10000 50 5000
K2 XYZ 300 30000 200 20000
K3 MNO 50 500 NULL NULL

How to get row values in to columns in sql server with based on id

I have the following table:
id Prefix FisrtName LastName
--------------------------------------------
123 Mr Lynn Berg
123 Ms Madeline Owen
123 Mrs Zelenia Sellers
101 Mrs Jesse Vincent
101 Mr Chaim Long
The result table should look like this
id name1 name2 name2
-----------------------------------------------------------
123 Mr Lynn Berg Ms MadelineOwen Mrs Zelenia Sellers
101 Mrs Jesse Vincent Mr Chaim Long
How could I achieve this result in SQL Server? Can I use pivot function?
Please help
You can also do conditional aggregation :
select id, max(case when seq = 1 then Name end) as Name1,
max(case when seq = 2 then Name end) as Name2,
max(case when seq = 3 then Name end) as Name3
from (select id, concat(Prefix,' ',FisrtName,' ',LastName) as Name,
row_number() over (partition by id order by (select null)) as seq
from table
) t
group by id;
Try this simple pivot:
declare #tbl table (id int, Prefix varchar(3), FirstName varchar(20), LastName varchar(20));
insert into #tbl values
(123, 'Mr', 'Lynn', 'Berg'),
(123, 'Ms', 'Madeline', 'Owen'),
(123, 'Mrs', 'Zelenia', 'Sellers'),
(101, 'Mrs', 'Jesse', 'Vincent'),
(101, 'Mr', 'Chaim', 'Long');
select id, [1] [Name1], [2] [Name2], [3] [Name3] from (
select id,
ROW_NUMBER() over (partition by id order by (select null)) rn,
Prefix + ' ' + FirstName + ' ' + LastName [FullName]
from #tbl
) a pivot (
max(fullname) for rn in ([1],[2],[3])
) b;
CREATE TABLE #Table1 (
id INT
,Prefix VARCHAR(3)
,FisrtName VARCHAR(8)
,LastName VARCHAR(7)
);
INSERT INTO #Table1
VALUES (
123
,'Mr'
,'Lynn'
,'Berg'
)
,(
123
,'Ms'
,'Madeline'
,'Owen'
)
,(
123
,'Mrs'
,'Zelenia'
,'Sellers'
)
,(
101
,'Mrs'
,'Jesse'
,'Vincent'
)
,(
101
,'Mr'
,'Chaim'
,'Long'
)
SELECT *
FROM #Table1
SELECT id
,[1] [Name1]
,[2] [Name2]
,[3] [Name3]
FROM (
SELECT id
,CONCAT (
PREFIX
,FISRTNAME
,LASTNAME
) AS Namm
,ROW_NUMBER() OVER (
PARTITION BY id ORDER BY (
id
)
) AS rn
FROM #TABLE1
) a
pivot(max(Namm) FOR rn IN (
[1]
,[2]
,[3]
)) b

sql query to format the table

Here is my input table. I am trying to format the table as shown in the output below. Could you please help me with the sql query in mssql
id type code
100 A k20
100 A m30
100 B m30
100 B m30
101 B x10
101 B 20
102 A 101
Output Table
id A_CODE B_CODE
100 k20,m30 m30,m30
101 null x10,20
102 101 null
Many examples of PIVOT and String Aggregation, but here is an example of both
Example
Select *
From (
Select A.ID
,Item = [type]+'_CODE'
,Value = Stuff((Select Distinct ',' +[code] From YourTable Where [id]=A.[id] and [type]=A.[type] For XML Path ('')),1,1,'')
From (Select Distinct [id],[type] from YourTable) A
) Src
Pivot (max(Value) for Item in ([A_CODE],[B_CODE])) Pvt
Returns
ID A_CODE B_CODE
100 k20,m30 m30
101 NULL 20,x10
102 101 NULL
Try this:
SELECT D.id
,STUFF(
(SELECT ',' + A_Code
FROM (
SELECT id,code A_Code FROM #Tab WHERE type='A'
)E
WHERE E.id=D.id FOR XML PATH ('')) , 1, 1, '') A_Code
,STUFF(
(SELECT ',' + B_Code
FROM (
SELECT id,code B_Code FROM #Tab WHERE type='B'
)E
WHERE E.id=D.id FOR XML PATH ('')) , 1, 1, '') B_Code
FROM(
SELECT id
,CASE WHEN type='A' THEN code END A_Code
,CASE WHEN type='B' THEN code END B_Code
FROM #Tab
)D
GROUP BY D.id
Output:
id A_Code B_Code
100 k20,m30 m30,m30
101 NULL x10,20
102 101 NULL

How can I select distinct by one column?

I have a table with the columns below, and I need to get the values if COD is duplicated, get the non NULL on VALUE column. If is not duplicated, it can get a NULL VALUE. Like the example:
I'm using SQL SERVER.
This is what I get:
COD ID VALUE
28 1 NULL
28 2 Supermarket
29 1 NULL
29 2 School
29 3 NULL
30 1 NULL
This is what I want:
COD ID VALUE
28 2 Supermarket
29 2 School
30 1 NULL
What I'm tryin' to do:
;with A as (
(select DISTINCT COD,ID,VALUE from CodId where ID = 2)
UNION
(select DISTINCT COD,ID,NULL from CodId where ID != 2)
)select * from A order by COD
You can try this.
DECLARE #T TABLE (COD INT, ID INT, VALUE VARCHAR(20))
INSERT INTO #T
VALUES(28, 1, NULL),
(28, 2 ,'Supermarket'),
(29, 1 ,NULL),
(29, 2 ,'School'),
(29, 3 ,NULL),
(30, 1 ,NULL)
;WITH CTE AS (
SELECT *, RN= ROW_NUMBER() OVER (PARTITION BY COD ORDER BY VALUE DESC) FROM #T
)
SELECT COD, ID ,VALUE FROM CTE
WHERE RN = 1
Result:
COD ID VALUE
----------- ----------- --------------------
28 2 Supermarket
29 2 School
30 1 NULL
Another option is to use the WITH TIES clause in concert with Row_Number()
Example
Select top 1 with ties *
from YourTable
Order By Row_Number() over (Partition By [COD] order by Value Desc)
Returns
COD ID VALUE
28 2 Supermarket
29 2 School
30 1 NULL
I would use GROUP BY and JOIN. If there is no NOT NULL value for a COD than it should be resolved using the OR in JOIN clause.
SELECT your_table.*
FROM your_table
JOIN (
SELECT COD, MAX(value) value
FROM your_table
GROUP BY COD
) gt ON your_table.COD = gt.COD and (your_table.value = gt.value OR gt.value IS NULL)
If you may have more than one non null value for a COD this will work
drop table MyTable
CREATE TABLE MyTable
(
COD INT,
ID INT,
VALUE VARCHAR(20)
)
INSERT INTO MyTable
VALUES (28,1, NULL),
(28,2,'Supermarket'),
(28,3,'School'),
(29,1,NULL),
(29,2,'School'),
(29,3,NULL),
(30,1,NULL);
WITH Dups AS
(SELECT COD FROM MyTable GROUP BY COD HAVING count (*) > 1 )
SELECT MyTable.COD,MyTable.ID,MyTable.VALUE FROM MyTable
INNER JOIN dups ON MyTable.COD = Dups.COD
WHERE value IS NOT NULL
UNION
SELECT MyTable.COD,MyTable.ID,MyTable.VALUE FROM MyTable
LEFT JOIN dups ON MyTable.COD = Dups.COD
WHERE dups.cod IS NULL

Compare two number SQL

In SQL,I am trying to compare two numbers in the same field. Both numbers contain different information, but for some technical reason they are same. The problem is when exist one sub-string of length 5 and another of length 4 and the last 4 digits of both are same.I want to get the first one with length 5.
Example:
--------------------------------
|ID | Number| Description |
---------------------------------
| 1 | 12345 | Project X,Ready |
---------------------------------
| 2 | 2345 | Project X,onDesign |
---------------------------------
I should always get 12345(or biggest one) if exist numbers with last 4 digits same. Is there any CASE or CTE statement which can give me an easy resolution for this issue?
Try this:
SELECT Id
,Number
,Description
FROM (
SELECT Id
,Number
,Description
,rank() OVER (PARTITION BY right(cast([Number] AS VARCHAR(20)), 4) ORDER BY Number DESC) AS Ranking
FROM YourTable
) InnerTable
WHERE ranking = 1
Here is an example with not exists:
DECLARE #t TABLE
(
ID INT ,
Number INT ,
Description VARCHAR(100)
)
INSERT INTO #t
VALUES ( 1, 12345, 'Project 1' ),
( 2, 2345, 'Project 2' ),
( 3, 77777, 'Project 3' ),
( 4, 7777, 'Project 4' ),
( 5, 88888, 'Project 5' ),
( 6, 9999, 'Project 6' )
SELECT * FROM #t t1
WHERE NOT EXISTS(SELECT * FROM #t t2
WHERE t2.ID <> t1.ID AND
CAST(t2.Number AS VARCHAR(10)) LIKE '%' + CAST(t1.Number AS VARCHAR(10)))
Output:
ID Number Description
1 12345 Project 1
3 77777 Project 3
5 88888 Project 5
6 9999 Project 6
So you need to join using last 4 digits. You could do this by using simple MOD operator. It's used as a percentage sign in SQL Server.
SELECT 12345 % 10000;
This outputs 2345. Exactly what we are looking for.
So we could build the following query to use that calculation:
DECLARE #Test TABLE
(
ID INT
, Number INT
, Description VARCHAR(500)
);
INSERT INTO #Test(ID, Number, Description)
VALUES (1, 12345, 'Project X,Ready')
, (2, 2345, 'Project X,onDesign');
SELECT T1.*
FROM #Test AS T1
INNER JOIN #Test AS T2
ON T2.Number = T1.Number % 10000
WHERE T2.Number <> T1.Number;
Output:
╔════╦════════╦═════════════════╗
║ ID ║ Number ║ Description ║
╠════╬════════╬═════════════════╣
║ 1 ║ 12345 ║ Project X,Ready ║
╚════╩════════╩═════════════════╝
Note that I've added WHERE T2.Number <> T1.Number. It eliminates equal numbers, because SELECT 2345 % 10000 is 2345 as well.
Update
This could be done using ROW_NUMBER()
;WITH Data (ID, Number, Description, RN)
AS (
SELECT ID
, Number
, Description
, ROW_NUMBER() OVER (PARTITION BY Number % 10000 ORDER BY Number DESC)
FROM #Test
)
SELECT *
FROM Data
WHERE RN = 1;
This will do the classic row_number stuff. It will partition windows by Number % 10000, which means that 12345 and 2345 will fall under same window and the highest number will always come first.
Try this:
SELECT DISTINCT A.*
FROM [Tablename] AS A
INNER JOIN [Tablename] AS B
ON B.Number =RIGHT(A.Number,4)
WHERE B.Number <> A.Number;
RIGHT(A.Number,4) will compare the last 4 digits and will give the output
The query might be RDBMS spesific. For example with MSSQL you can do like this:
SELECT *
FROM myTable AS d1
WHERE NOT EXISTS ( SELECT *
FROM myTable AS d2
WHERE SUBSTRING(d2.number, 2, 4) = d1.number );
EDIT: Ah, you edited and it is an INT! Then you can use the % operator instead of substring.
Sample with CTE:
DECLARE #dummy TABLE
(
id INT IDENTITY
PRIMARY KEY ,
number INT ,
[description] VARCHAR(20)
);
INSERT #dummy ( [number], [description] )
VALUES ( 12345, 'P' ),
( 22345, 'P' ),
( 2345, 'P' ),
( 3456, 'P' ),
( 13456, 'P' ),
( 4567, 'P' );
WITH d AS (
SELECT MAX(number) AS maxNum
FROM #dummy AS [d]
GROUP BY [d].[number] % 10000
)
SELECT d1.*
FROM #dummy AS [d1]
INNER JOIN d ON d.[maxNum] = d1.[number];