SQL How can I order the data using distinct? - sql

I have the following Table 'tbl1'.
declare #tbl1 table ( col1 varchar(32))
insert into #tbl1
values ( 'C1' )
, ( 'B1' )
, ( 'X1' )
, ( 'A1' )
, ( 'B1' )
, ( 'C1' )
, ( 'B1' )
, ( 'A1' )
, ( 'X1' )
, ( 'C1' )
, ( 'D1' )
I tried the following query
select distinct col1
from #tbl1
order by col1
The output should come in the following order, and remove all the duplicate value
C1
B1
X1
A1
D1

You need to specify the order for your items, as others have noted.
Also you need a subquery and grouping to be able to sort over the minimal order column.
declare #source table (id int not null identity primary key, name nvarchar(3));
insert into #source
values ( 'C1' )
, ( 'B1' )
, ( 'X1' )
, ( 'A1' )
, ( 'B1' )
, ( 'C1' )
, ( 'B1' )
, ( 'A1' )
, ( 'X1' )
, ( 'C1' )
, ( 'D1' )
;
with grouped as
(
select min(id) as minId, name from #source
group by name
)
select name from grouped order by minId;
The query could be rewritten without CTE:
select grouped.name from
(select min(id) as minId, name from #source group by name) grouped
order by grouped.minId;
This yields exactly the result you requested.

To repeat what others have pointed out in comments: if you specify no ORDER, then the order of results is not guaranteed. The fact that you get your results in a certain order currently should be treated as coincidental. If you want a certain ordering in your results, you have to be explicit about it!
As an interesting note, in my experience this is especially important if you're doing a DISTINCT in your query, because depending on the statistics for your tables, the engine may or may not decide that ordering the data to execute the DISTINCT is in fact the best possible plan.
Given that you mention a very explicit ordering requirement...
The output should come in the following order, and remove all the duplicate value
C1
B1
X1
A1
D1
...you should make that explicit in your query:
SELECT DISTINCT
*,
CASE
WHEN Col1 = 'C1' THEN 0
WHEN Col1 = 'B1' THEN 1
WHEN Col1 = 'X1' THEN 2
WHEN Col1 = 'A1' THEN 3
WHEN Col1 = 'D1' THEN 4
ELSE 5
END AS SortColumn
FROM tbl1
ORDER BY SortColumn

(extension of Jeroen response)
If your "key" values are dynamic, you normally should have a separate table with the order of the keys...
declare #sort table (id varchar(10), ord int)
insert into #sort
values ( 'C1', 1 )
, ( 'X1', 2 )
, ( 'B1', 3 )
, ( 'A1', 4 )
, ( 'D1', 5 )
then you join/subquery on that table to calculate the SortColumn
-- join
SELECT DISTINCT
t.*,
s.ord SortColumn
FROM #tbl1 t
LEFT JOIN #sort s ON S.id = t.col1
ORDER BY SortColumn
-- subquery
SELECT DISTINCT
t.*,
(SELECT s.ord FROM #sort s WHERE s.id = t.col1) SortColumn
FROM #tbl1 t
ORDER BY SortColumn

declare #tbl1 table ( col1 varchar(32))
insert into #tbl1
values ( 'Basic Salary' )
, ( 'HRA' )
, ( 'OtherAllowance' )
, ( 'PF' )
, ( 'Basic Salary' )
, ( 'HRA' )
, ( 'Other Allowance' )
, ( 'PF' )
, ( 'ESIC' )
, ( 'Basic Salary' )
, ( 'HRA' )
, ( 'Other Allowance' )
, ( 'PF' )
, ( 'Basic Salary' )
select distinct col1
from #tbl1
order by col1

Related

Return random value for each row from different table

I'm trying to get random name for each row, but it just shows same random value for every row. What am I missing?
SELECT TOP (1000) v.id,v.[Name], RandomName
FROM [V3_Priva].[dbo].[Vehicle] v
cross join
(Select top 1 ISNULL([Description_cs-CZ], [Description]) RandomName
from crm.Enumeration e
join crm.EnumerationType et on e.EnumerationType_FK = et.Id
where EnumerationType_FK = 12
order by NEWID()) RandomName
Result table
Try using something like the following to drive your lookup.
DECLARE #Rows AS TABLE ( I INT NOT NULL )
DECLARE #Vals AS TABLE ( ID INT IDENTITY, Val NVARCHAR(255) NOT NULL )
INSERT INTO #Rows
VALUES ( 0 )
, ( 1 )
, ( 2 )
, ( 3 )
, ( 4 )
, ( 5 )
, ( 6 )
, ( 7 )
, ( 8 )
, ( 9 )
INSERT INTO #Vals
VALUES ( 'Apple' )
, ( 'Banana' )
, ( 'Peach' )
, ( 'Plum' )
, ( 'Pear' )
WITH cte AS ( SELECT *, ABS(CHECKSUM(NEWID())) % 5 ID FROM #Rows )
SELECT cte.I
, cte.ID
, V.ID
, V.Val
FROM cte
JOIN #Vals V ON V.ID = cte.ID + 1
ORDER BY I
This way new ID is generated for each row, rather than once for the lookup.

SQL - Prepend a value if missing

Code/data:
DECLARE #T TABLE
(
[Col1] VARCHAR(20)
, [RowNum] INT
) ;
INSERT INTO #T
VALUES
( N'second', 1 )
, ( N'fifth', 4 )
, ( N'fourth', 3 )
--, ( N'zzz', 1 )
, ( N'third', 2 )
---- OR when "zzz" is part of this list
--VALUES
-- ( N'second', 2 )
-- , ( N'fifth', 5 )
-- , ( N'fourth', 4 )
-- , ( N'zzz', 1 )
-- , ( N'third', 3 )
SELECT STUFF ((
SELECT ',' + [SQ].[Col1]
FROM
(
SELECT N'zzz' AS [Col1]
, 1 AS [RowNum]
UNION
SELECT [Col1]
, [RowNum]
FROM #T
) AS [SQ]
FOR XML PATH ( '' ), TYPE
).[value] ( '.', 'varchar(MAX)' ), 1, 1, ''
) ;
Current output:
fifth,fourth,second,third,zzz
Goal:
Prepend "zzz," in the output string if missing in the 2nd part of the union AND the values should be in ASC ordered based on the values specified in [rownum] field defined in the 2nd part of the union. If "zzz" exists in the 2nd part of the input already (it will always be RowNum 1 in that case), it should return it only once as the first value.
Expected output:
zzz,second,third,fourth,fifth
UPDATED the requirement due to an error on my part when creating this post. Updated code/data represents more accurate scenario. Please note the RowNum seq in the 2nd part of the UNION, it also starts with 1, but this time, it might or might not be associated to "zzz" Basically, I want to prepend "zzz" in the comma-delimited & ordered output if it doesn't exist.
Hope the below one will help you.
SELECT ',' + [SQ].[Col1]
FROM
(
SELECT N'first' AS [Col1],1 AS [RowNum]
UNION
SELECT [ABC].[Col1],[ABC].[RowNum]
FROM
(
VALUES
( N'second', 2 )
, ( N'fifth', 5 )
, ( N'fourth', 4 )
--, ( N'first', 1 )
, ( N'third', 3 )
) AS [ABC] ( [Col1], [RowNum] )
) AS [SQ]
ORDER BY [RowNum]
FOR XML PATH ( '' ), TYPE
).[value] ( '.', 'varchar(MAX)' ), 1, 1, ''
) ;
Returns an output
first,second,third,fourth,fifth
Attached the Answer for the updated Scenario-
DECLARE #T TABLE
(
[Col1] VARCHAR(20)
, [RowNum] INT
) ;
INSERT INTO #T
VALUES
( N'second', 1 )
, ( N'fifth', 4 )
, ( N'fourth', 3 )
--, ( N'zzz', 1 )
, ( N'third', 2 )
---- OR when "zzz" is part of this list
--VALUES
-- ( N'second', 2 )
-- , ( N'fifth', 5 )
-- , ( N'fourth', 4 )
-- , ( N'zzz', 1 )
-- , ( N'third', 3 )
SELECT STUFF ((
SELECT ',' + [SQ].[Col1]
FROM
(
SELECT N'zzz' AS [Col1]
, 0 AS [RowNum]
UNION
SELECT [Col1]
, [RowNum]
FROM #T
) AS [SQ]
ORDER BY [RowNum]
FOR XML PATH ( '' ), TYPE
).[value] ( '.', 'varchar(MAX)' ), 1, 1, ''
) ;
Returns
zzz,second,third,fourth,fifth
Common Table Expressions (CTEs) provide a handy way of breaking queries down into simpler steps. Note that you can view the results of each step by switching out the last select statement.
with
Assortment as (
-- Start with the "input" rows.
select Col1, RowNum
from ( values ( N'second', 2 ), ( N'fifth', 5 ), ( N'fourth', 4 ),
-- ( N'first', 1 ),
( N'third', 3 ) ) ABC ( Col1, RowNum ) ),
ExtendedAssortment as (
-- Conditionally add "first".
select Col1, RowNum
from Assortment
union all -- Do not remove duplicate rows.
select N'first', 1
where not exists ( select 42 from Assortment where Col1 = N'first' ) )
-- Output the result.
-- Intermediate results may be seen by uncommenting one of the alternate select statements.
-- select * from Assortment;
-- select * from ExtendedAssortment;
select Stuff(
( select N',' + Col1 from ExtendedAssortment order by RowNum for XML path(N''), type).value( N'.[1]', 'NVarChar(max)' ),
1, 1, N'' ) as List;
The same logic can be performed using tables for input:
-- Rows to be included in the comma delimited string.
declare #Input as Table ( Col1 NVarChar(20), RowNum Int );
insert into #Input ( Col1, RowNum ) values
( N'second', 2 ), ( N'fifth', 5 ),
--( N'ZZZ', 17 ), -- Test row.
( N'fourth', 4 ), ( N'third', 3 );
select * from #Input;
-- Mandatory value that must appear in the result. One row only.
declare #Mandatory as Table ( Col1 NVarChar(20), RowNum Int );
-- By using the maximum negative value for an Int this value will be prepended
-- (unless other rows happen to have the same RowNum value).
insert into #Mandatory ( Col1, RowNum ) values ( N'ZZZ', -2147483648 );
select * from #Mandatory;
-- Process the data.
with
AllRows as (
select Col1, RowNum
from #Input
union all
select Col1, RowNum
from #Mandatory
where not exists ( select 42 from #Mandatory as M inner join #Input as I on M.Col1 = I.Col1 ) )
-- Output the result.
-- Intermediate results may be seen by uncommenting the alternate select statement.
--select * from AllRows;
select Stuff(
( select N',' + Col1 from AllRows order by RowNum for XML path(N''), type).value( N'.[1]', 'NVarChar(max)' ),
1, 1, N'' ) as List;

converting rows into columns in sql server

I wonder if anyone can help: I want to convert rows into columns. This is the original table:
I have tried using pivot but this case it is too complex for me.
declare #Table AS TABLE
(
TYPE VARCHAR(100) ,
SERIE VARCHAR(100) ,
CUR1 INT,
CUR2 INT
)
INSERT #Table
( TYPE, SERIE, CUR1, CUR2)
VALUES
( 'CORP', 'S1' ,2122,322 ),
( 'CORP', 'S2' ,321,546 ),
( 'SER', 'S1',543,788 ),
( 'SER', 'S2' ,655, 988 )
I expect the output to be like the attached table:
Please try this, a variant of this will help:-
;with cte as (
select SERIE, [CORP] as [CORP_CUR1], [SER] as [SER_CUR1] from (
select type , serie, cur1 from #Table)
as d
pivot
( max(cur1) for [type] in ( [CORP], [SER]) ) as p
),
ct as (
select SERIE, [CORP] as [CORP_CUR2], [SER] as [SER_CUR2] from (
select type , serie, cur2 from #Table)
as d
pivot
( max(cur2) for [type] in ( [CORP], [SER]) ) as p
)
select cte.SERIE, cte.[CORP_CUR1], cte.[SER_CUR1], ct.[CORP_CUR2], ct.[SER_CUR2] from cte inner join ct on cte.SERIE=ct.SERIE

Insert multiple row different column value

This is my existing table
In this table, each user has their own respective data according to their Status. Each of the user will surely have Status 1.
Now, there are 3 Status to be stored for every user.
Was trying to make every user to have 3 Status, by inserting new row of user copying their Status 1 data, such that:
User Ali currently only have Status 1 and its data, so need insert a new
row Ali with Status 2 and copy along the data from Status 1, again,
insert a new row Ali with Status 3 and copy along the data from
Status 1.
User John currently only have Status 1 and 2, so need insert a new
row John with Status 3 and copy along the data from Status 1.
continue same pattern with other user
Expected result:
I would use CROSS JOIN and NOT EXISTS
with data as
(
select name,
column1,
column2
from your_table
where status = 1
), cross_join_data as
(
select d1.name, t.status, d1.column1, d1.column2
from data d1
cross join
(
select 1 status
union
select 2 status
union
select 3 status
) t
where not exists (
select 1
from your_table d2
where d2.name = d1.name and
d2.status = t.status
)
)
select *
from your_table
union all
select *
from cross_join_data
dbfiddle demo
This should work
with cte as (
select
[Name], coalesce(max(iif([Status]=1, [Column1], null)), max(iif([Status]=2, [Column1], null)), max(iif([Status]=3, [Column1], null))) col1
, coalesce(max(iif([Status]=1, [Column2], null)), max(iif([Status]=2, [Column2], null)), max(iif([Status]=3, [Column2], null))) col2
from
MyTable
group by [Name]
)
--insert into MyTable
select
cte.[Name], nums.n, cte.col1, cte.col2
from
cte
cross join (values (1),(2),(3)) nums(n)
left join MyTable on cte.[Name]=MyTable.[Name] and n=MyTable.[Status]
where
MyTable.[Status] is null
This works if data is not nullable
declare #table table (name varchar(10), status int, data int);
insert into #table values
('a', 1, 2)
, ('a', 2, 5)
, ('a', 3, 7)
, ('b', 1, 5)
, ('b', 2, 6)
, ('c', 1, 3)
select stats.status as statusStats
, tn.name as nameTN
, t.status as statusData, t.name, t.data
, ISNULL(t.data, t1.data) as 'fillInData'
from (values (1),(2),(3)) as stats(status)
cross join (select distinct name from #table) tn
left join #table t
on t.status = stats.status
and t.name = tn.name
join #table t1
on t1.name = tn.name
and t1.status = 1
order by tn.name, stats.status
Here is what I would do:
CREATE TABLE #existingtable (Name VARCHAR(50), Status INT, Column1 VARCHAR (10), Column2 VARCHAR(10));
INSERT INTO #existingtable (Name,Status,Column1,Column2) Values('Ali',1,'100','90');
INSERT INTO #existingtable (Name,Status,Column1,Column2) Values('John',1,'20','200');
INSERT INTO #existingtable (Name,Status,Column1,Column2) Values('John',2,'80','90');
INSERT INTO #existingtable (Name,Status,Column1,Column2) Values('Ming',1,'54','345');
INSERT INTO #existingtable (Name,Status,Column1,Column2) Values('Mei',1,'421','123');
INSERT INTO #existingtable (Name,Status,Column1,Column2) Values('Mei',3,'24','344');
SELECT * FROM #existingtable;
WITH CTE (Name,Column1,Column2)
AS
(
SELECT DISTINCT NAME,COLUMN1,COLUMN2
FROM #existingtable
)
, CTE2 (NAME,Status,Column1,Column2)
AS
(
SELECT NAME,1 AS STATUS,COLUMN1,COLUMN2
FROM CTE
UNION
SELECT NAME,2 AS STATUS,COLUMN1,COLUMN2
FROM CTE
UNION
SELECT NAME,3 AS STATUS,COLUMN1,COLUMN2
FROM CTE
)
INSERT INTO #existingtable (Name,Status,Column1,Column2)
SELECT C.Name,C.Status,C.Column1,C.Column2
FROM CTE2 AS C
LEFT JOIN #existingtable AS E
ON C.NAME = E.Name
AND C.Status = E.Status
WHERE E.Status IS NULL
SELECT * FROM #existingtable
ORDER BY Name, status
This has 2 edits. Initial edit added a where clause to the CTE
Second edit added the values added by the OP

Performing a custom sort which included order by clause in derived table

I am facing a complex situation where I am aware of the approach which can solve the problem but the order by clause in my derived table is messing up the custom sort. Here are my input and output details and what I have tried.
Schema :-
Input :-
CREATE TABLE Test( Rowname VARCHAR(10), Col1 DATETIME, Col2 DATETIME, Col3 DATETIME, Col4 DATETIME );
INSERT INTO Test VALUES( 'Row1', '2016-01-14', '2016-01-08', '2016-01-30', '2016-01-01' );
INSERT INTO Test VALUES( 'Row2', '2016-01-02', '2016-01-01', '2016-01-18', '2016-01-15' );
Expected Output :-
RowName Result
Row1 Col4,Col2,Col1,Col3
Row2 Col2,Col1,Col4,Col3
What I have tried?
WITH CTE(RowName,Colmn,RN) AS
(
SELECT RowName,Colmn,ROW_NUMBER() OVER( PARTITION BY RowName ORDER BY RowName ) AS RN
FROM
(
( SELECT RowName,Col1 AS Col,'Col1' AS Colmn FROM Test )
UNION ALL
( SELECT RowName,Col2 AS Col,'Col2' AS Colmn FROM Test )
UNION ALL
( SELECT RowName,Col3 AS Col,'Col3' AS Colmn FROM Test )
UNION ALL
( SELECT RowName,Col4 AS Col,'Col4' AS Colmn FROM Test )
) Z
ORDER BY RowName,Col
)
SELECT RowName,
MAX( ( CASE WHEN RN = 1 THEN Colmn END ) ) + ',' +
MAX( ( CASE WHEN RN = 2 THEN Colmn END ) ) + ',' +
MAX( ( CASE WHEN RN = 3 THEN Colmn END ) ) + ',' +
MAX( ( CASE WHEN RN = 4 THEN Colmn END ) ) AS Result
FROM CTE
GROUP BY RowName;
Note :-
The ORDER BY RowName,Col Clause in the inner query/derived table is failing as it is not allowed in SQL Server. If I don't use this ORDER BY then how can I perform custom sort without using ORDER BY clause?
Your order by should be defined in the over clause:
WITH CTE(RowName,Colmn,RN) AS
(
SELECT
RowName,Colmn,ROW_NUMBER() OVER(
PARTITION BY RowName
ORDER BY RowName, Col -- add Col here
) AS RN
FROM
(
( SELECT RowName,Col1 AS Col,'Col1' AS Colmn FROM Test )
UNION ALL
( SELECT RowName,Col2 AS Col,'Col2' AS Colmn FROM Test )
UNION ALL
( SELECT RowName,Col3 AS Col,'Col3' AS Colmn FROM Test )
UNION ALL
( SELECT RowName,Col4 AS Col,'Col4' AS Colmn FROM Test )
) Z
-- ORDER BY RowName,Col -- remove this line
)
SELECT RowName,
MAX( ( CASE WHEN RN = 1 THEN Colmn END ) ) + ',' +
MAX( ( CASE WHEN RN = 2 THEN Colmn END ) ) + ',' +
MAX( ( CASE WHEN RN = 3 THEN Colmn END ) ) + ',' +
MAX( ( CASE WHEN RN = 4 THEN Colmn END ) ) AS Result
FROM CTE
GROUP BY RowName;
if you add col into to the order by in the row_number it will order the way you want.
ROW_NUMBER() OVER( PARTITION BY RowName ORDER BY RowName, col )
declare #test TABLE( Rowname VARCHAR(10), Col1 DATETIME, Col2 DATETIME, Col3 DATETIME, Col4 DATETIME );
INSERT INTO #test VALUES( 'Row1', '2016-01-14', '2016-01-08', '2016-01-30', '2016-01-01' );
INSERT INTO #test VALUES( 'Row2', '2016-01-02', '2016-01-01', '2016-01-18', '2016-01-15' );
;with cte as(
select a.Rowname, 'Col' + b.ID as ColName, b.Col from #test as a
outer apply (select * from (values ('1', Col1), ('2', Col2), ('3', Col3), ('4', Col4)) as t(ID, Col)) as b
)
select Distinct Rowname,
(Select ColName + ', ' From cte as b
Where b.Rowname=a.Rowname
order by b.Col for xml path(''), type).value('.', 'varchar(30)') as ColList
from cte as a