Case when rowcount - sql

SELECT distinct ID from table
Result
Letter
1 A
2 B
How can I make the select display the following when the result is as above:
What I was thinking but not working yet:
SELECT CASE WHEN ##ROWCOUNT(ID) = 2 THEN 'AB'
ELSE ID END AS ID
FROM table
Result would be
Letter
1 AB

Your requirements really aren't clear.
Based on various assumptions, this might give you what you want.
SELECT Count(*) OVER (PARTITION BY 937)
, CASE WHEN Count(*) OVER (PARTITION BY 937) = 2 THEN
'AB'
ELSE
id
END As id
, id
FROM your_table

Try:
Select
CASE WHEN (ROW_NUMBER() OVER (ORDER BY [ID])) = 2 THEN 'AB'
ELSE [ID] END AS [ID]
FROM table
group by
[ID]
What's above will also only work if [ID] and 'AB' are the same data type. If not, then you'll need to cast [ID] to a varchar in your ELSE statement. That'd be: ELSE cast([ID] as varchar(8)) END AS [ID]

Am not sure what you are trying to achieve. But to me it looks like this is what you are trying..
Declare a Variable and initialize it with Distinct Count of ID and use it in select
Declare #cnt int
SELECT #cnt=count(distinct ID) from table
SELECT CASE WHEN #cnt = 2 THEN 'AB'
ELSE ID END AS ID
FROM table

Related

Extract Data from a large string to table using SQL

I'm trying to extract data strings from a long string using SQL query.
the string "'35522':{'item_id':'35522','sku':'deded','RowTotal':37.5,'qty':2"
I am trying to create a loop query that extracts data from the string.
The desired output is a table with columns (item_id,sku, RowTotal,qty) and each line will be extracted from the string above in the relevant column.
Im trying to create a function that will do that but currentley im not close.
Can you please assist me with ideas?
DECLARE #String VARCHAR(4000) =
'{:{item_id:35522,sku:deep-line-elixir,RowTotal:37.5,qty:2},
:{item_id:35527,sku:self-care-pamper-pack,RowTotal:158,qty:2},
:{item_id:35531,sku:neck-chest-rejuvenating-serum,RowTotal:21.87,qty:1},
:{item_id:35534,sku:pm-recovery-night-cream,RowTotal:23.75,qty:1},couponCode:,itemsQty:6}"'
DECLARE #b VARBINARY(4000) = CONVERT(varbinary(4000),#string)
DECLARE #StartPos int=9
DECLARE #Len tinyint=13
;WITH C (Orig,Startpos,Value) AS (
SELECT #b,#StartPos,CONVERT(VARCHAR,SUBSTRING(#b,#StartPos,#Len))
UNION ALL
SELECT #b,C.Startpos+#Len,CONVERT(VARCHAR,SUBSTRING(#b,C.StartPos+#Len,#Len)) FROM C
WHERE C.Startpos+#Len < = LEN(#b)
)
select C.Value from c where c.value like 'item%'
This is my code so far
The following approach splits the data into rows using STRING_SPLIT before replacing the additional characters and using case expressions to match field values. The temporary table is used to generate a row_num which can be used to order values when generating the group_num used to group the related values across multiple rows into a single related row
DECLARE #SampleString VARCHAR(4000) =
'{:{item_id:35522,sku:deep-line-elixir,RowTotal:37.5,qty:2},
:{item_id:35527,sku:self-care-pamper-pack,RowTotal:158,qty:2},
:{item_id:35531,sku:neck-chest-rejuvenating-serum,RowTotal:21.87,qty:1},
:{item_id:35534,sku:pm-recovery-night-cream,RowTotal:23.75,qty:1},couponCode:,itemsQty:6}"';
create table #temp_values (id int identity(1,1), value VARCHAR(200) );
insert into #temp_values (value) SELECT value FROM STRING_SPLIT(REPLACE(REPLACE(#SampleString,'{',''),'}',''),',');
WITH split_data AS (
SELECT id,value FROM #temp_values
),
extracted_data_raw AS (
SELECT
id as row_num,
CASE
WHEN value LIKE '%item_id%' THEN 1
WHEN value LIKE '%sku:%' THEN 2
WHEN value LIKE '%RowTotal:%' THEN 3
WHEN value LIKE '%qty:%' AND value NOT LIKE '%itemsQty%' THEN 4
END as type_num,
CASE
WHEN value LIKE '%item_id%' THEN TRIM(REPLACE(value,':item_id:',''))
END as item_id,
CASE WHEN value LIKE '%sku:%' THEN TRIM(REPLACE(value,'sku:','')) END as sku,
CASE WHEN value LIKE '%RowTotal:%' THEN TRIM(REPLACE(value,'RowTotal:','')) END as RowTotal,
CASE WHEN value LIKE '%qty:%' AND value NOT LIKE '%itemsQty%' THEN TRIM(REPLACE(value,'qty:','')) END as qty,
value
FROM split_data
)
,extracted_data_clean AS (
SELECT
MAX(item_id) as item_id,
MAX(sku) as sku,
MAX(RowTotal) as RowTotal,
MAX(qty) as qty
FROM (
SELECT
ROW_NUMBER() OVER (PARTITION BY type_num ORDER BY row_num) group_num,
item_id,
sku,
RowTotal,
qty
FROM
extracted_data_raw
WHERE
type_num IS NOT NULL
) t
GROUP BY group_num
)
select * from extracted_data_clean
Outputs:
item_id
sku
RowTotal
qty
35522
deep-line-elixir
37.5
2
35527
self-care-pamper-pack
158
2
35531
neck-chest-rejuvenating-serum
21.87
1
35534
pm-recovery-night-cream
23.75
1
Working Demo DB Fiddle
Edit 1:
You also referenced "'35522':{'item_id':'35522','sku':'deded','RowTotal':37.5,'qty':2" in your question. The following should work for both samples while also converting to respective data types:
DECLARE #SampleString VARCHAR(4000) =
'{"35522":{item_id:35522,sku:deep-line-elixir,RowTotal:37.5,qty:2},
"35522":{item_id:35527,sku:self-care-pamper-pack,RowTotal:158,qty:2},
"35522":{item_id:35531,sku:neck-chest-rejuvenating-serum,RowTotal:21.87,qty:1},
"35522":{item_id:35534,sku:pm-recovery-night-cream,RowTotal:23.75,qty:1},couponCode:,itemsQty:6}"';
create table #temp_values (id int identity(1,1), value VARCHAR(200) );
insert into #temp_values (value) SELECT value FROM STRING_SPLIT(REPLACE(REPLACE(#SampleString,'{',''),'}',''),',');
WITH split_data AS (
SELECT id,value FROM #temp_values
),
extracted_data_raw AS (
SELECT
id as row_num,
CASE
WHEN value LIKE '%item_id%' THEN 1
WHEN value LIKE '%sku:%' THEN 2
WHEN value LIKE '%RowTotal:%' THEN 3
WHEN value LIKE '%qty:%' AND value NOT LIKE '%itemsQty%' THEN 4
END as type_num,
CASE
WHEN value LIKE '%item_id%' THEN CONVERT(INT,TRIM(
REPLACE(SUBSTRING(
value,CHARINDEX('item_id',value),LEN(value)
),'item_id:','')
))
END as item_id,
CASE WHEN value LIKE '%sku:%' THEN TRIM(REPLACE(value,'sku:','')) END as sku,
CASE WHEN value LIKE '%RowTotal:%' THEN CONVERT(NUMERIC(10,2),TRIM(REPLACE(value,'RowTotal:',''))) END as RowTotal,
CASE WHEN value LIKE '%qty:%' AND value NOT LIKE '%itemsQty%' THEN CONVERT(NUMERIC(10,2),TRIM(REPLACE(value,'qty:',''))) END as qty,
value
FROM split_data
)
,extracted_data_clean AS (
SELECT
MAX(item_id) as item_id,
MAX(sku) as sku,
MAX(RowTotal) as RowTotal,
MAX(qty) as qty
FROM (
SELECT
ROW_NUMBER() OVER (PARTITION BY type_num ORDER BY row_num) group_num,
item_id,
sku,
RowTotal,
qty
FROM
extracted_data_raw
WHERE
type_num IS NOT NULL
) t
GROUP BY group_num
)
select * from extracted_data_clean;
drop table #temp_values;
Working Demo DB Fiddle
Let me know if this works for you.

SQL - populate new column according to data in row above

I need to populate a new column in a table known as RowType, where if the ID column contains the same ID value as the one above RowType is populated with 'D', if the value is new then RowType is populate with 'H', how would the SQL code look to be able to do this?
I.e should look something like below:
RowType (to be populated), ID (already there)
H, 1
D, 1
D, 1
H, 2
D, 2
H, 3
D, 3
D, 3
Thanks
You can use Row_Number and case
select *, RowType = case when Row_Number() over (partition by id order by id) = 1 then 'H' else 'D' End from #yourid
Your input table:
create table #yourId (id int)
insert into #yourid (id) values
(1)
,(1)
,(1)
,(2)
,(2)
,(3)
,(3)
,(3)
Use ROW_NUMER concept :
CREATE TABLE #table(Id INT)
INSERT INTO #table(Id)
SELECT 1 UNION ALL
SELECT 1 UNION ALL
SELECT 1 UNION ALL
SELECT 2 UNION ALL
SELECT 2 UNION ALL
SELECT 3 UNION ALL
SELECT 3 UNION ALL
SELECT 3
SELECT CASE WHEN RowType = 1 THEN 'H' ELSE 'D' END RowType , Id
FROM
(
SELECT ROW_NUMBER() OVER (PARTITION BY Id ORDER BY id) RowType , Id
FROM #table
) A
Please try...
UPDATE tableName
SET RowType = CASE
WHEN ( ID = LAG( ID ) OVER ( ORDER BY ID ) ) THEN 'D'
ELSE 'H'
END
If you have any questions or comments, then please feel free to post a Comment accordingly.
Further Reading
https://learn.microsoft.com/en-us/sql/t-sql/functions/lag-transact-sql (for information on LAG()).
It may not be the best solution, however it can point you somewhere, and it works.
Go through the code carfuly and make sure you understand this.
create table yourTable (RowType char, id int)
insert into yourTable (RowType, id) values
('',1)
,('',1)
,('',1)
,('',2)
,('',2)
,('',3)
,('',3)
,('',3)
select
row_number() over (order by id) as rowNumber,
RowType,
id
into #tempTable
from yourTable
declare #maxRow int = (select max(rowNumber) from #tempTable)
declare #currentRow int = 1
while (#currentRow <= #maxRow)
begin
if (#currentRow = 1)
begin
update #tempTable
set RowType = 'H'
where rowNumber = #currentRow
end
else
begin
if (select id from #tempTable where rowNumber = #currentRow) = (select id from #tempTable where rowNumber = #currentRow - 1)
begin
update #tempTable
set RowType = 'D'
where rowNumber = #currentRow
end
else
begin
update #tempTable
set RowType = 'H'
where rowNumber = #currentRow
end
end
set #currentRow = #currentRow +1
end
-- update data in actual table, you can do below if only those two columns exist in table !!!
delete from yourTable
-- insert into table from updated temp table
insert into yourTable
select RowType, ID
from #tempTable
select * from yourTable
select * from #tempTable
-- drop temp table
drop table #tempTable

How to get a non matching record in single row

Table1
ID
001
002
001
001
001
...
I want to check the id from table1 where id should be even. If id is different then i need to return 2 else 1
How to write a query for this?
For IDs
SELECT (CASE WHEN [ID]%2 = 1 THEN 1 ELSE 2 END)
FROM [table]
For ID COUNT :
SELECT (CASE WHEN COUNT([ID])%2 = 1 THEN 1 ELSE 2 END)
FROM [table]
GROUP BY [ID]
Please check this.
declare #t table (id varchar(50))
insert into #t values('001'),('001'),('002'),('002'),('001'),('002'),('002')
SELECT
CASE WHEN cast( [ID] as int) %2 = 1 THEN 1 ELSE 2 END oddOrEven
FROM #t
--for counting
;with cte as
(
SELECT [ID]%2 value,
CASE cast( [ID] as int) %2 when 1 THEN count(1) else count(2) END oddCount
FROM #t
group by id
)
select * from cte
If I understand the question correctly, a CASE statement is not necessary here. I'm assuming you want to return 2 when ID is even, and 1 when ID is odd? As long as there aren't any non-digit characters in the values of the ID column, you can do the following:
SELECT [ID], 2 - CAST([ID] AS int) % 2
FROM Table1
If you want to return 2 when ID is odd, and 1 when it is even (sorry, that wasn't clear from the question), then you can do this:
SELECT [ID], CAST([ID] AS int) % 2 + 1
FROM Table1

T-Sql count string sequences over multiple rows

How can I find subsets of data over multiple rows in sql?
I want to count the number of occurrences of a string (or number) before another string is found and then count the number of times this string occurs before another one is found.
All these strings can be in random order.
This is what I want to achieve:
I have one table with one column (columnx) with data like this:
A
A
B
C
A
B
B
The result I want from the query should be like this:
2 A
1 B
1 C
1 A
2 B
Is this even possible in sql or would it be easier just to write a little C# app to do this?
Since, as per your comment, you can add a column that will unambiguously define the order in which the columnx values go, you can try the following query (provided the SQL product you are using supports CTEs and ranking functions):
WITH marked AS (
SELECT
columnx,
sortcolumn,
grp = ROW_NUMBER() OVER ( ORDER BY sortcolumn)
- ROW_NUMBER() OVER (PARTITION BY columnx ORDER BY sortcolumn)
FROM data
)
SELECT
columnx,
COUNT(*)
FROM marked
GROUP BY
columnx,
grp
ORDER BY
MIN(sortcolumn)
;
You can see the method in work on SQL Fiddle.
If sortcolumn is an auto-increment integer column that is guaranteed to have no gaps, you can replace the first ROW_NUMBER() expression with just sortcolumn. But, I guess, that cannot be guaranteed in general. Besides, you might indeed want to sort on a timestamp instead of an integer.
I dont think you can do it with a single select.
You can use AdventureWorks cursor:
create table my_Strings
(
my_string varchar(50)
)
insert into my_strings values('A'),('A'),('B'),('C'),('A'),('B'),('B') -- this method will only work on SQL Server 2008
--select my_String from my_strings
declare #temp_result table(
string varchar(50),
nr int)
declare #myString varchar(50)
declare #myLastString varchar(50)
declare #nr int
set #myLastString='A' --set this with the value of your FIRST string on the table
set #nr=0
DECLARE string_cursor CURSOR
FOR
SELECT my_string as aux_column FROM my_strings
OPEN string_cursor
FETCH NEXT FROM string_cursor into #myString
WHILE ##FETCH_STATUS = 0 BEGIN
if (#myString = #myLastString) begin
set #nr=#nr+1
set #myLastString=#myString
end else begin
insert into #temp_result values (#myLastString, #nr)
set #myLastString=#myString
set #nr=1
end
FETCH NEXT FROM string_cursor into #myString
END
insert into #temp_result values (#myLastString, #nr)
CLOSE string_cursor;
DEALLOCATE string_cursor;
select * from #temp_result
Result:
A 2
B 1
C 1
A 1
B 2
Try this :
;with sample as (
select 'A' as columnx
union all
select 'A'
union all
select 'B'
union all
select 'C'
union all
select 'A'
union all
select 'B'
union all
select 'B'
), data
as (
select columnx,
Row_Number() over(order by (select 0)) id
from sample
) , CTE as (
select * ,
Row_Number() over(order by (select 0)) rno from data
) , result as (
SELECT d.*
, ( SELECT MAX(ID)
FROM CTE c
WHERE NOT EXISTS (SELECT * FROM CTE
WHERE rno = c.rno-1 and columnx = c.columnx)
AND c.ID <= d.ID) AS g
FROM data d
)
SELECT columnx,
COUNT(1) cnt
FROM result
GROUP BY columnx,
g
Result :
columnx cnt
A 2
B 1
C 1
A 1
B 2

How to know if all the cells have the same value in some column

How to know if all the cells have the same value in some column (title changed)
I want to have a bit scalar value that tells me if all the values in a column equal something:
DECLARE #bit bit
SELECT #bit = TRUEFORALL(Name IS NOT NULL) FROM Contact
UPDATE
I now realized that I actually don't need the TrueForAll, what I do need is to make sure, that all values in a column are equal, for example, I want to know whether all Group.Items have the same price.
Why not?
select count( distinct price) from table
If returns 1, all values are the same... Add
where price is not null
if need be
For your updated requirement something like this would appear to do what you want:
DECLARE #IsSameGroup bit
SELECT #IsSameGroup = CASE WHEN COUNT(*) > 1 THEN 0 ELSE 1 END
FROM (SELECT Name FROM Contact GROUP BY Name) groups
When the count is greater the 1 you have two different names (or prices depending on what you group on)
Not very good for NULLs, but 2008 can do:
SELECT 1 WHERE 'Blue' = ALL ( SELECT Color FROM dbo.Hat )
OR
DECLARE #bit bit
SET #bit =
CASE ( SELECT 1 WHERE 'Blue' = ALL ( SELECT Color FROM dbo.Hat ))
WHEN 1 THEN 1 ELSE 0 END
UPDATE
All same color
SET #bit =
CASE(
SELECT 1 WHERE
(SELECT TOP(1) Color FROM dbo.Hat) = ALL ( SELECT Color FROM dbo.Hat )
)
WHEN 1 THEN 1 ELSE 0 END
Maybe this?
DECLARE #bit bit
if exists(SELECT Name FROM Contact WHERE Name IS NULL)
SET #bit = 0
ELSE
SET #bit = 1
This solves your first question:
SELECT
CASE
WHEN EXISTS(
SELECT 1
FROM Contact
WHERE Name IS NULL
) THEN 0
ELSE 1
END
ADDED:
This will solve your second:
SELECT
CASE
WHEN EXISTS(
SELECT TOP 1 1 FROM (
SELECT
ItemGroupName,
COUNT(Price) AS CNT
FROM ItemGroup
GROUP BY ItemGroupName
HAVING COUNT(Price) > 1
) t
) THEN 0
ELSE 1
END
By the way, when you use the exists function, its better to SELECT 1 (a constant) so less data gets returned