Need help in rewriting the query using CASE statement - snowflake-cloud-data-platform

SELECT distinct
ID,
LOWER(IFF(REGEXP_COUNT(pos_id, '^[0-9]+$')= 1, NULL, pos_id)) as pos
FROM table1
WHERE date='2022-02-02'
AND pos_id is not null
AND id='12345';
When I run the above query, I'm getting results like
ID
POS
12345
894f4bb2597f
But when I run the query below where I have used CASE, I'm getting NULL values as well as NOT NULL values.
SELECT distinct
ID,
CASE WHEN REGEXP_COUNT(pos_id, '^[0-9]+$')= 1 AND pos_id IS NOT NULL THEN NULL ELSE pos_id
END as pos
FROM table1
WHERE date='2022-02-02'
AND pos_id is not null
AND id='12345';
ID
POS
12345
894f4bb2597f
12345
NULL
For one ID, I'm getting NULL as well not NULL values.
I need to remove pos_id is not null in the WHERE clause and add that in CASE statement.
How to rewrite this query using CASE statement by removing the condition - pos_id is not null from the WHERE clause?
I tried the below query using CASE statement but not getting the correct results:
SELECT distinct
ID,
CASE when REGEXP_COUNT(pos_id,'^[0-9]+$')=1 and
pos_id is not null
THEN null else pos_id end
FROM table1
WHERE date='2022-02-02';
When I use CASE, I'm getting the count as 1,455,345 ROWS
but when I use - LOWER(IFF(REGEXP_COUNT(pos_id, '^[0-9]+$')= 1, NULL, pos_id
I'm getting COUNT as 2768 rows

so the key point of you question is the final sentence:
When I use CASE, I'm getting the count as 1,455,345 ROWS but when I use - LOWER(IFF(REGEXP_COUNT(pos_id, '^[0-9]+$')= 1, NULL, pos_id I'm getting COUNT as 2768 rows
So take your SQL and pushing it together with some input trying to understand "why the results are different" etc etc.
And really you are asking why do I how more distinct values when I don't to lower then then when I do.
The point is case sensitive counts will always be same or great than case insensitive counts.
SELECT
column2 as pos_id,
LOWER(IFF(REGEXP_COUNT(pos_id, '^[0-9]+$')= 1, NULL, pos_id)) as pos_i,
CASE
WHEN REGEXP_COUNT(pos_id, '^[0-9]+$') = 1 AND pos_id IS NOT NULL THEN NULL
ELSE pos_id
END as pos_c
,lower(pos_c) as lower_pos_c
,count(distinct pos_i) over() as IFF_rows_count
,count(distinct pos_c) over() as CASE_rows_count
,count(distinct lower_pos_c) over() as LOWER_CASE_rows_count
FROM VALUES
(12345, '894f4bb2597f'),
(12346, '1234'),
(12346, null),
(123, 'aaa'),
(123, 'Aaa'),
(123, 'aAa'),
(123, 'aaA');
POS_ID
POS_I
POS_C
LOWER_POS_C
IFF_ROWS_COUNT
CASE_ROWS_COUNT
LOWER_CASE_ROWS_COUNT
894f4bb2597f
894f4bb2597f
894f4bb2597f
894f4bb2597f
2
5
2
1234
null
null
null
2
5
2
null
null
null
null
2
5
2
aaa
aaa
aaa
aaa
2
5
2
Aaa
aaa
Aaa
aaa
2
5
2
aAa
aaa
aAa
aaa
2
5
2
aaA
aaa
aaA
aaa
2
5
2

Related

SQL Check to see if any value in a list exists in another table

I have a temp table that looks something like this:
Record DepartmentId PositionId EmployeeId StatusId CustomerId
1 Null Null Null 4
2 7 454 Null Null
3 Null 454 Null 3
3 Null Null Null Null 214
3 Null Null Null Null 100
3 Null Null Null Null 312
4 Null Null Null Null 357
I inserted the above into the temp table from tables that looked like this:
Record Table Record-to-Department Record-to-Position
Record Name Record DepartmentId Record PositionId
1 Red 2 7 2 454
2 blue 3 454
3 Green
4 Purple
Record-To-Status Record-To-Customer
Record StatusId Record CustomerId
1 4 3 214
3 3 3 100
3 312
4 357
I have an employee whose record looks something like this:
EmployeeId DepartmentId PositionId StatusId
342 7 454 4
Employee Customers:
EmployeeId CustomerId
342 357
342 95
342 720
In this scenario, it would return Record 1 (because it matches the StatusId), Record 2 (because it matches both the DepartmentId and the PositionId), but it would not return Record 3 because it only matches the PositionId and not the StatusId, and it would return RecordId 4 because one of the Employee CustomerIds matches the CustomerId on Record 4.
I got part of this answer on another question enter link description here (please forgive me I am new and trying to figure out how to ask everything I need to know), but I can't figure out how to handle the multi-records.
I tried selecting the Employees customer Id's into a table variable and then attempted to use the Coalesce like this:
Declare #Customers table(CustomerId int)
INSERT INTO #Customers(CustomerId)
SELECT DISTINCT S.CustomerId
FROM employee_Customers
Select * from tbl
WHERE
COAlesce(StatusId,#StatusId)=#StatusId AND
COALESCE(DepartmentId,#DepartmentId)=#DepartmentId AND
Coalesce(PositionId,#PositionId)=#PositionId AND
Coalesce(EmployeeCompanyId,#EmployeeCompanyId) = #EmployeeCompanyId AND
COALESCE((Select CustomerId from tbl_Requirement_to_Customer),(Select CustomerId from #Customers)) = (Select CustomerId from #Customers)
But I receive the error "Subquery Returned more than 1 value".
I have a possible solution you can try. I don't think it will be plug-and-play but hopefully you can adapt it to your situation. I am using just the data as presented in your temp table, your employee record and your Employee-customers correlation.
The basic logic is to join your temp table to the employee(s) using or condition, but then to get a count of populated values, and compare this count to a count of the number of matching values, which must be at least the first count and greater than zero.
This returns your desired output:
select t.*
from Temp t
left join emp e on e.DepartmentId=t.DepartmentId or e.PositionId=t.PositionId or e.EmployeeId=t.EmployeeId or e.StatusId=t.StatusId
outer apply (
select case when exists (
select * from EmployeeCustomers ec join emp e on e.EmployeeId=ec.EmployeeId where ec.CustomerID=t.CustomerId
) then 1 else 0 end CustomerIdMatch
)c
outer apply (
values (
Iif(t.departmentId is null,0,1) +
Iif(t.PositionId is null,0,1) +
Iif(t.EmployeeId is null,0,1) +
Iif(t.StatusId is null,0,1) +
c.CustomerIdMatch
))x(Cnt)
outer apply (
values (
Iif(t.departmentId=e.DepartmentId,1,0) +
Iif(t.PositionId=e.PositionId,1,0) +
Iif(t.EmployeeId=e.EmployeeId,1,0) +
Iif(t.StatusId=e.StatusId,1,0) +
c.CustomerIdMatch
))y(Cnt2)
where cnt2>=cnt and cnt2>0
See working DB<>Fiddle

How to select changed columns

The Problem
I'm trying to detect and react to changes in a table where each update is being recorded as a new row with some values being the same as the original, some changed (the ones I want to detect) and some NULL values (not considered changed).
For example, given the following table MyData, and assuming the OrderNumber is the common value,
ID OrderNumber CustomerName PartNumber Qty Price OrderDate
1 123 Acme Corp. WG301 4 15.02 2020-01-02
2 456 Base Inc. AL337 7 20.15 2020-02-03
3 123 NULL WG301b 5 19.57 2020-01-02
If I execute the query for OrderNumber = 123 I would like the following data returned:
Column OldValue NewValue
ID 1 3
PartNumber WG301 WG301b
Qty 4 5
Price 15.02 19.57
Or possibly a single row result with only the changes filled, like this (however, I would strongly prefer the former format):
ID OrderNumber CustomerName PartNumber Qty Price OrderDate
3 NULL NULL WG301b 5 19.57 NULL
My Solution
I have not had a chance to test this, but I was considering writing the query with the following approach (pseudo-code):
select
NewOrNull(last.ID, prev.ID) as ID,
NewOrNull(last.OrderNumber, prev.OrderNumber) as OrderNumber
NewOrNull(last.CustomerName, prev.CustomerName) as CustomerName,
...
from last row with OrderNumber = 123
join previous row where OrderNumber = 123
Where the function NewOrNull(lastVal, prevVal) returns NULL if the values are equal or lastVal value is NULL, otherwise the lastVal.
Why I'm Looking for an Answer
I'm afraid that the ugly join, the number of calls to the function, and the procedural approach may make this approach not scalable. Before I start down the rabbit hole, I was wondering...
The Question
...are there any other approaches I should try, or any best practices to solving this specific type of problem?
I came up with a solution for the second (less preferred) format:
The Data
Using the following data:
INSERT INTO MyData
([ID], [OrderNumber], [CustomerName], [PartNumber], [Qty], [Price], [OrderDate])
VALUES
(1, 123, 'Acme Corp.', 'WG301', '4', '15.02', '2020-01-02'),
(2, 456, 'Base Inc.', 'AL337', '7', '20.15', '2020-02-03'),
(3, 123, NULL, 'WG301b', '5', '19.57', '2020-01-02'),
(4, 123, 'ACME Corp.', 'WG301b', NULL, NULL, '2020-01-02'),
(6, 456, 'Base Inc.', NULL, '7', '20.15', '2020-02-05');
The Function
This function returns the updated value if it has changed, otherwise NULL:
CREATE FUNCTION dbo.NewOrNull
(
#newValue sql_variant,
#oldValue sql_variant
)
RETURNS sql_variant
AS
BEGIN
DECLARE #ret sql_variant
SELECT #ret = CASE
WHEN #newValue IS NULL THEN NULL
WHEN #oldValue IS NULL THEN #newValue
WHEN #newValue = #oldValue THEN NULL
ELSE #newValue
END
RETURN #ret
END;
The Query
This query returns the history of changes for the given order number:
select dbo.NewOrNull(new.ID, old.ID) as ID,
dbo.NewOrNull(new.OrderNumber, old.OrderNumber) as OrderNumber,
dbo.NewOrNull(new.CustomerName, old.CustomerName) as CustomerName,
dbo.NewOrNull(new.PartNumber, old.PartNumber) as PartNumber,
dbo.NewOrNull(new.Qty, old.Qty) as Qty,
dbo.NewOrNull(new.Price, old.Price) as Price,
dbo.NewOrNull(new.OrderDate, old.OrderDate) as OrderDate
from MyData new
left join MyData old
on old.ID = (
select top 1 ID
from MyData pre
where pre.OrderNumber = new.OrderNumber
and pre.ID < new.ID
order by pre.ID desc
)
where new.OrderNumber = 123
The Result
ID OrderNumber CustomerName PartNumber Qty Price OrderDate
1 123 Acme Corp. WG301 4 15.02 2020-01-02
3 (null) (null) WG301b 5 19.57 (null)
4 (null) ACME Corp. (null) (null) (null) (null)
The Fiddle
Here's the SQL Fiddle that shows the whole thing in action.
http://sqlfiddle.com/#!18/b720f/5/0

T-SQL Get Values from Lookup table and use in view / stored procedure

I couldn't find that via search, so I guess I am not asking it the right way, so help is welcome.
We have a lookup table:
Id Name
------------------
1 "Test"
2 "New"
3 "InProgress"
Table2:
StatusId SomethingElse
1
2
Table 1
ID Other Other StatusId (Fkey to Table2) ...
Then we have a view that selects from several tables and one of the columns is a CASE Statement:
SELECT * FROM Table1 t1 -- has million records
CASE When t1.StatusId = 1 THEN (SELECT Name from LOOKUP table where ID = 1) END --'Test'
CASE When t1.StatusId = 2 THEN (SELECT Name from LOOKUP table where ID = 2) END --'New'
CASE When t3.Date is not null THEN (SELECT Name from LOOKUP table where ID = 3) END --'In Progress'
-- AND ALSO the case look at other tables another 5-6 tables and there are conditions from there
INNER JOIN Table2 t2 on ...
INNER JOIN Table3 t3 on ...
As you see these are really static values.
I want to load them once into variables, e.g.
#LookUp1 = SELECT [NAME] FROM LookUP WHERE Id = 1,
#LookUp2 = SELECT [NAME] FROM LookUP WHERE Id = 2
and replace the select in the CASE statement to this:
When StatusId = 1 THEN #LookUp
When StatusId = 2 THEN #LookUp2
The view loops through millions of records and it gets really slow to do the select from Lookup table for every row.
Why not simply use a join?
SELECT <columns list from main table>, Lt.Name
FROM <main table> As Mt -- Do not use such aliases in real code!
JOIN <SecondaryTable> As St -- this represents your Table3
ON <condition>
[LEFT] JOIN <Lookup table> As Lt
ON Mt.StatusId = Lt.Id
OR (Lt.Id = 3 AND St.Date is not null)
Of course, replace <columns list from main table> with the actual columns list, <main table> with the name of the main table and so on.
The join might be an inner or left join, depending on the nullability of the StatusId column in the main table and if it's nullable, on what you want to get in such cases (either a row with null name or no row at all).
I've put together a little demonstration to show you exactly what I mean.
Create and populate sample tables (Please save us this step in your future questions):
CREATE TABLE LookUp (Id int, Name varchar(10));
INSERT INTO LookUp (Id, Name) VALUES
(1, 'Test'), (2, 'New'), (3, 'InProgress');
CREATE TABLE Table1 (Id int not null, StatusId int null);
INSERT INTO Table1(Id, StatusId)
SELECT n, CASE WHEN n % 3 = 0 THEN NULL ELSE (n % 3) END
FROM
(
SELECT TOP 30 ROW_NUMBER() OVER(ORDER BY ##SPID) As n
FROM sys.objects
) tally
CREATE TABLE Table3
(
Id int not null,
Date date null
)
INSERT INTO Table3 (Id, Date)
SELECT Id, CASE WHEN StatusId IS NULL AND Id % 4 = 0 THEN GetDate() END
FROM Table1
The query:
SELECT Table1.Id,
Table1.StatusId,
Table3.Date,
LookUp.Name
FROM Table1
JOIN Table3
ON Table1.Id = Table3.Id
LEFT JOIN LookUp
ON Table1.StatusId = LookUp.Id
OR (LookUp.Id = 3 AND Table3.Date IS NOT NULL)
Results:
Id StatusId Date Name
1 1 NULL Test
2 2 NULL New
3 NULL NULL NULL
4 1 NULL Test
5 2 NULL New
6 NULL NULL NULL
7 1 NULL Test
8 2 NULL New
9 NULL NULL NULL
10 1 NULL Test
11 2 NULL New
12 NULL 27.06.2019 InProgress
13 1 NULL Test
14 2 NULL New
15 NULL NULL NULL
16 1 NULL Test
17 2 NULL New
18 NULL NULL NULL
19 1 NULL Test
20 2 NULL New
21 NULL NULL NULL
22 1 NULL Test
23 2 NULL New
24 NULL 27.06.2019 InProgress
25 1 NULL Test
26 2 NULL New
27 NULL NULL NULL
28 1 NULL Test
29 2 NULL New
30 NULL NULL NULL
You can also see a live demo on rextester.
Create a SQL function which return Name according to Id.
Create FUNCTION [dbo].[GetLookUpValue]
(
#Id int
)
RETURNS varchar(500)
AS BEGIN
return(Select Name from LOOKUP_table with(nolock) where Id=#Id)
END

how to sum the all column and all nulls get display into one (NA) in sql with out using replace

cid c2id
1 null
1 null
1 null
null 1
null 2
null 3
2 null
3 null
null 4
null 2
output:-
cid c2id
3 NA
NA 6
5 NA
NA 6
if add any one record it will also add and give out put like same
use two queries, one to sum, other to count nulls, then union them. There is no command which does it at once.
Here is one way that you could try
SELECT DISTINCT
ISNULL(CONVERT(VARCHAR, SUM(cid) OVER(PARTITION BY cid)), 'NA') cid,
ISNULL(CONVERT(VARCHAR, SUM(c2id) OVER(PARTITION BY cid)), 'NA') c2id
FROM <table_name>;
Result :
cid c2id
3 NA
NA 6
Assuming you have ID as Identity primary key column in your table for the order.
Query:
; WITH CTE AS
(
SELECT *,
CASE WHEN C2id IS NULL
THEN (SELECT top 1 ID FROM cid n WHERE n.c2id IS NOT NULL AND n.ID > CID.ID)
WHEN Cid IS NULL
THEN (SELECT top 1 ID FROM cid n WHERE n.cid IS NOT NULL AND n.ID > CID.ID)
END AS NextID
FROM CID
)
SELECT
CASE WHEN MIN(c2id) IS NULL THEN SUM(cid) ELSE -1 END AS cid,
CASE WHEN MIN(cid) IS NULL THEN SUM(c2id) ELSE -1 END AS c2id
FROM CTE
GROUP BY NextID
ORDER BY CASE WHEN NEXTID IS NULL THEN 1 ELSE 0 END, NextID
Sample Output:
cid c2id
3 -1
-1 6
5 -1
-1 6
You can replace -1 with NA. It calculates the next ID for required group and calculates sum in that group.
Please evaluate performance and correctness of query before using it.

SQLServer Order By IGNORE NULL values

For some reason I can't change the table or update the data for now. Here the problem :
I have menu_user table below :
userID menuID
(null) 2
(null) 3
1 3
2 1
3 2
4 5
5 0
userID and menuID not duplicated. The problem is how to ORDER BY userID, menuID but when userID has NULL value, it will look for another row that has same menuID and place it after this row. menuID just have max 2 same value and if it have, another one must be NULL
The order result expected :
userID menuID
1 3
(null) 3
2 1
3 2
(null) 2
4 5
5 0
Here the script sample :
CREATE TABLE [dbo].[menu_user](
[userID] [int] NULL,
[menuID] [int] NULL
);
INSERT [dbo].[menu_user] ([userID], [menuID]) VALUES (NULL, 3);
INSERT [dbo].[menu_user] ([userID], [menuID]) VALUES (1, 3);
INSERT [dbo].[menu_user] ([userID], [menuID]) VALUES (2, 1);
INSERT [dbo].[menu_user] ([userID], [menuID]) VALUES (3, 2);
INSERT [dbo].[menu_user] ([userID], [menuID]) VALUES (4, 5);
INSERT [dbo].[menu_user] ([userID], [menuID]) VALUES (5, 0);
INSERT [dbo].[menu_user] ([userID], [menuID]) VALUES (NULL, 2);
ADDED
If possible I want this script as View (just SELECT with No Variable).
This seems to do the trick. You need to do something to relate multiple rows together. Here I've chosen to use a left join:
select
m1.*
from
menu_user m1
left join
menu_user m2
on
m1.userID is null and
m1.menuID = m2.menuID and
m2.userID is not null
order by
COALESCE(m1.userID,m2.userID),m1.userID desc
Result:
userID menuID
----------- -----------
1 3
NULL 3
2 1
3 2
NULL 2
4 5
5 0
Hopefully you can see how it's achieving its aims.
Check this, ordering is bit messed up but this gives you your desired result.
SELECT * FROM menu_user mu
ORDER BY mu.menuID,
CASE WHEN mu.userID IS NULL THEN mu.menuID END
Using the left join solution will produce duplicates when there are more than 1 non-null Users for a menuID. This is another method.
select userID, menuID
From (
select *, Case when a.UseriD is not null then cast(a.userID as float) else
(select max(b.userID) + 0.1 from menu_user b where a.menuID = b.menuID and a.userID is null) end as SortCol
from menu_user a
) c Order by SortCol
Try this query:
SELECT *
FROM menu_user mu
WHERE userID IS NOT NULL
ORDER BY mu.menuID

Resources