How to use IN inside of CASE - sql

SELECT T_MW.*
INTO #temp
FROM T_MWP T_MW
WHERE T_MW.CompanyID = 2
AND (
CASE
WHEN #Currency IS NOT NULL
THEN (
T_MW.Currency IN (
SELECT *
FROM #Currency
)
)
END
)
AND (
CASE
WHEN #Asset IS NOT NULL
THEN (
T_MW.Asset IN (
SELECT *
FROM #Asset
)
)
END
)
Here #Asset is a table containing assets and #Currency is a temporay table containing Currency. #Asset and #Currency are created using following query
SELECT *
INTO #Asset
FROM dbo.Split(#Asset, ',');

Use OR Instead of Case
SELECT
T_MW.*
INTO #temp
FROM T_MWP T_MW
WHERE T_MW.CompanyID = 2
AND
(
(
#Currency IS NOT NULL
AND
T_MW.Currency IN
(
SELECT * FROM #Currency
)
)
OR
#Currency IS NULL
)
AND
(
(
#Asset IS NOT NULL
AND
T_MW.Asset IN
(
SELECT * FROM #Asset
)
)
OR
#Asset IS NULL
)

SELECT
T_MW.*
INTO #temp
FROM T_MWP T_MW
WHERE T_MW.CompanyID = 2
AND
(
(
#Currency IS NOT NULL
OR
T_MW.Currency IN
(
SELECT * FROM #Currency
)
)
)
AND
(
(
#Asset IS NOT NULL
OR
T_MW.Asset IN
(
SELECT * FROM #Asset
)
)
)

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

Can correlated subquery following is not null check be replaced with joins?

Can this correlated subquery, where the subquery follows is not null check on the foreign key, be replaced with joins? Example:
select * from TableABC as t
where
(t.label_id is null or t.label_id in ( select t1.id from Labels as t1 where t1.type = '123'))
and
(t.tag_id is null or t.tag_id in ( select t2.id from Tags as t2 where t2.type = '123'))
Described by words: Let's say, I'm looking for all records where if they have label reference defined then the label must be of certain type; same apply for tags.
Or could this query be improved by other means?
It is intended for TSQL (MS SQL).
UPDATE:
I have added table aliases as hinted by HABO. Hope it will improve readability.
I would be inclined to write this as:
select t.*
from TableABC abc
where (abc.label_id is null or
exists (select 1 from labels where l.id = abc.label_id and l.type = 123)
) and
(abc.tag_id is null or
exists (select 1 from tags t where t.id = abc.tag_id and t.type = 123)
);
Then I would be sure I have in index on labels(id, type) and tags(id, type) (if the ids are not already primary keys).
However, your version would probably also have a reasonable execution plan with the right indexes.
I'm not sure that this is an improvement, but it does use left outer join instead of correlated subqueries.
-- Sample data.
declare #TableABC as Table( ABCId Int Identity, LabelId Int, TagId Int );
declare #Labels as Table( LabelId Int Identity, Label VarChar(3) );
declare #Tags as Table( TagId Int Identity, Tag VarChar(3) );
insert into #Labels ( Label ) values ( '123' ), ( '12' ), ( '123' );
insert into #Tags ( Tag ) values ( '123' ), ( '213' ), ( '123' ), ( '312' );
insert into #TableABC ( LabelId, TagId ) values
( 1, 1 ), ( 1, 2 ), ( 1, 3 ), ( 1, 4 ),
( 2, 1 ), ( 2, 2 ), ( 2, 3 ), ( 2, 4 ),
( 3, 1 ), ( 3, 2 ), ( 3, 3 ), ( 3, 4 ),
( NULL, 1 ), ( NULL, 3 ), ( 1, NULL ), ( 3, NULL ), ( NULL, NULL );
select ABC.ABCId, ABC.LabelId, ABC.TagId,
L.LabelId as L_LabelId, L.Label as L_Label,
case when ABC.LabelId is NULL or L.Label = '123' then '<<<' else '' end as 'L_Match',
T.TagId as T_TagId, T.Tag as T_Tag,
case when ABC.TagId is NULL or T.Tag = '123' then '<<<' else '' end as 'T_Match'
from #TableABC as ABC left outer join
#Labels as L on L.LabelId = ABC.LabelId left outer join
#Tags as T on T.TagId = ABC.TagId;
-- "Original" query:
select *
from #TableABC
where ( LabelId is null or LabelId in ( select LabelId from #Labels where Label = '123' ) ) and
( TagId is null or TagId in ( select TagId from #Tags where Tag = '123' ) );
-- Left outer joins:
select ABC.*
from #TableABC as ABC left outer join
#Labels as L on L.LabelId = ABC.LabelId and L.Label = '123' left outer join
#Tags as T on T.TagId = ABC.TagId and T.Tag = '123'
where ( ABC.LabelId is NULL or L.LabelId is not NULL ) and ( ABC.TagId is NULL or T.TagId is not NULL );
Tip: Always use helpful table aliases with joins and apply them to all columns.
With a union
select *
from TableABC
where TableABC.label_id is null
union all
select *
from TableABC
join Labels
on TableABC.label_id = Lables.id
and Lablestype = '123'
on TableABC.label_id = Lables.id or TableABC.label_id is null would return too many rows

SQL How can I order the data using distinct?

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