How could I create combinations of removed dashes in SQL Server string? - sql-server

I've been looking for a table-valued function for SQL Server that can do the following:
Input string: A-B-C-D (or any length string with dash-separated characters, like XX-W2-ZZZ-AAA-777-888)
Output strings (all combinations of removed dashes):
ABCD, A-BCD, AB-CD, ABC-D, A-B-CD, AB-C-D, A-B-C-D
I've noticed the pattern follows a binary counter with respect to which dash should be removed to generate the combinations. In the example above, you could remove the dashes associated with the 0 positions of 000, 001, 010, 011, 100, 101, 110, and 111. However I don't see how to do this in SQL Server. Have any of you tackled this challenge before? Thank you!

For this first section, I'm going to split the string into table / columns using XML.
DECLARE #Test TABLE
( ID INT,
NAME VARCHAR(MAX)
)
INSERT INTO #Test
VALUES( 1, 'XX-W2-ZZZ-AAA-777-888' )
DECLARE #ColSplit TABLE
(
Id int,
Col1 VARCHAR(MAX),
Col2 VARCHAR(MAX),
Col3 VARCHAR(MAX),
Col4 VARCHAR(MAX),
Col5 VARCHAR(MAX),
Col6 VARCHAR(MAX),
Col7 VARCHAR(MAX),
Col8 VARCHAR(MAX)
)
;WITH FormSplitXML
AS
(
Select Id, Name,
CONVERT(XML,'<r><n>' + REPLACE(Name, '-', '</n><n>') + '</n></r>') AS X
FROM #Test
)
INSERT INTO #ColSplit
SELECT Id,
i.value('n[1]','varchar(100)') AS Col1,
i.value('n[2]','varchar(100)') AS Col2,
i.value('n[3]','varchar(100)') AS Col3,
i.value('n[4]','varchar(100)') AS Col4,
i.value('n[5]','varchar(100)') AS Col5,
i.value('n[6]','varchar(100)') AS Col6,
i.value('n[7]','varchar(100)') AS Col7,
i.value('n[8]','varchar(100)') AS Col8
FROM FormSplitXML Spt
CROSS APPLY Spt.X.nodes('/r') x(i)
This forms a table with this output:
SELECT * FROM #ColSplit
Id Col1 Col2 Col3 Col4 Col5 Col6 Col7 Col8
1 XX W2 ZZZ AAA 777 888 NULL NULL
Next I am going to make a bit array table of all of the possible bit combinations:
DECLARE #BitTable TABLE
(
v int,
V1 BIT,
V2 BIT,
V3 BIT,
V4 BIT,
V5 BIT,
V6 BIT,
V7 BIT,
V8 BIT
)
Declare #t table (v integer not null primary key, check(v >= 0));
;WITH
a AS (SELECT 1 AS i UNION ALL SELECT 1),
b AS (SELECT 1 AS i FROM a AS x, a AS y),
c AS (SELECT 1 AS i FROM b AS x, b AS y),
d AS (SELECT 1 AS i FROM c AS x, c AS y),
e AS (SELECT 1 AS i FROM d AS x, d AS y),
f AS (SELECT 1 AS i FROM e AS x, e AS y),
numbers AS
(
SELECT TOP(255)
ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS number
FROM f
)
Insert into #t
SELECT number FROM numbers
INSERT INTO #BitTable
( v, V1, V2, V3, V4, V5, V6, V7, V8 )
SELECT
v,
CONVERT(BIT, v & 1) AS V1,
CONVERT(BIT, v & 2) AS V2,
CONVERT(BIT, v & 4) AS V3,
CONVERT(BIT, v & 8) AS V4,
CONVERT(BIT, v & 16) AS V5,
CONVERT(BIT, v & 32) AS V6,
CONVERT(BIT, v & 64) AS V7,
CONVERT(BIT, v & 128) AS V8
FROM
#t
Here is the output of the bittable (only the first 10 rows, it keeps going to 255)
SELECT * FROM #BitTable
v V1 V2 V3 V4 V5 V6 V7 V8
1 1 0 0 0 0 0 0 0
2 0 1 0 0 0 0 0 0
3 1 1 0 0 0 0 0 0
4 0 0 1 0 0 0 0 0
5 1 0 1 0 0 0 0 0
6 0 1 1 0 0 0 0 0
7 1 1 1 0 0 0 0 0
8 0 0 0 1 0 0 0 0
9 1 0 0 1 0 0 0 0
10 0 1 0 1 0 0 0 0
Now using the bit table and the split columns, I am going to put a string together of all of the possible combinations:
SELECT
bt.*,
t.*,
CASE WHEN bt.V1 = 1 THEN ISNULL(t.Col1,'') + '-' ELSE t.Col1 END +
CASE WHEN bt.V2 = 1 THEN ISNULL(t.Col2,'') + '-' ELSE ISNULL(t.Col2,'') END +
CASE WHEN bt.V3 = 1 THEN ISNULL(t.Col3,'') + '-' ELSE ISNULL(t.Col3,'') END +
CASE WHEN bt.V4 = 1 THEN ISNULL(t.Col4,'') + '-' ELSE ISNULL(t.Col4,'') END +
CASE WHEN bt.V5 = 1 THEN ISNULL(t.Col5,'') + '-' ELSE ISNULL(t.Col5,'') END +
CASE WHEN bt.V6 = 1 THEN ISNULL(t.Col6,'') + '-' ELSE ISNULL(t.Col6,'') END +
CASE WHEN bt.V7 = 1 THEN ISNULL(t.Col7,'') + '-' ELSE ISNULL(t.Col7,'') END +
CASE WHEN bt.V8 = 1 THEN ISNULL(t.Col8,'') + '-' ELSE ISNULL(t.Col8,'') END
FROM #BitTable bt
CROSS JOIN #ColSplit t
Here is the output (snipped to 10 rows, it goes to 255):
v V1 V2 V3 V4 V5 V6 V7 V8 Id Col1 Col2 Col3 Col4 Col5 Col6 Col7 Col8 (No column name)
1 1 0 0 0 0 0 0 0 1 XX W2 ZZZ AAA 777 888 NULL NULL XX-W2ZZZAAA777888
2 0 1 0 0 0 0 0 0 1 XX W2 ZZZ AAA 777 888 NULL NULL XXW2-ZZZAAA777888
3 1 1 0 0 0 0 0 0 1 XX W2 ZZZ AAA 777 888 NULL NULL XX-W2-ZZZAAA777888
4 0 0 1 0 0 0 0 0 1 XX W2 ZZZ AAA 777 888 NULL NULL XXW2ZZZ-AAA777888
5 1 0 1 0 0 0 0 0 1 XX W2 ZZZ AAA 777 888 NULL NULL XX-W2ZZZ-AAA777888
6 0 1 1 0 0 0 0 0 1 XX W2 ZZZ AAA 777 888 NULL NULL XXW2-ZZZ-AAA777888
7 1 1 1 0 0 0 0 0 1 XX W2 ZZZ AAA 777 888 NULL NULL XX-W2-ZZZ-AAA777888
8 0 0 0 1 0 0 0 0 1 XX W2 ZZZ AAA 777 888 NULL NULL XXW2ZZZAAA-777888
9 1 0 0 1 0 0 0 0 1 XX W2 ZZZ AAA 777 888 NULL NULL XX-W2ZZZAAA-777888
10 0 1 0 1 0 0 0 0 1 XX W2 ZZZ AAA 777 888 NULL NULL XXW2-ZZZAAA-777888
Here you go, hope it helps.

I was trying to find something that did not involve multiple loops and would be more set-based. A CROSS JOIN is good for combinations as that is what a Cross Join / Cartesian-product is. But I still had to resort to Dynamic SQL due to the variable nature of how many dashes there might be. "But you can't do Dynamic SQL in a function!" I keep hearing that, yet I am not entirely convinced due to the following SQLCLR TVF. It constructs a query that, for the example input of A-B-C-D, looks like:
SELECT CONCAT(tab1.part, tab2.part, tab3.part, tab4.part) AS [Combinations]
FROM (SELECT N'A') tab1(part)
CROSS JOIN (SELECT N'B' UNION ALL SELECT N'-B') tab2(part)
CROSS JOIN (SELECT N'C' UNION ALL SELECT N'-C') tab3(part)
CROSS JOIN (SELECT N'D' UNION ALL SELECT N'-D') tab4(part)
This dynamic construction makes use of the pattern of combinations being:
FirstElement + {Cartesian Product of no-dash and preceding-dash versions of remaining elements}
The .Net / C# code:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using System.Text;
using Microsoft.SqlServer.Server;
public class TVF
{
public static void ReturnCombo(object Combination, out SqlString Combo)
{
Combo = (string)Combination;
}
[Microsoft.SqlServer.Server.SqlFunction(FillRowMethodName = "ReturnCombo",
TableDefinition = "Combo NVARCHAR(500)", DataAccess = DataAccessKind.Read)]
public static IEnumerable GetCombinations([SqlFacet(MaxSize = -1)] SqlString DashedThingy)
{
List<string> _Combinations = new List<string>();
int _PartNum = 0;
StringBuilder _FirstPart = new StringBuilder("SELECT CONCAT(tab1.part");
StringBuilder _SecondPart = new StringBuilder(") AS [Combinations]\n");
foreach (string _Part in DashedThingy.Value.Split(new char[1] { '-' }))
{
_PartNum++;
if (_PartNum == 1)
{
_SecondPart.Append("FROM (SELECT N'").Append(_Part).AppendLine("') tab1(part)");
}
else
{
_FirstPart.Append(", tab").Append(_PartNum).Append(".part");
_SecondPart.Append("CROSS JOIN (SELECT N'").Append(_Part);
_SecondPart.Append("' UNION ALL SELECT N'-").Append(_Part);
_SecondPart.Append("') tab").Append(_PartNum).AppendLine("(part)");
}
}
SqlConnection _Connection = new SqlConnection("Context Connection = true;");
SqlCommand _Command = new SqlCommand();
_Command.Connection = _Connection;
_Command.CommandType = CommandType.Text;
_Command.CommandText = _FirstPart.ToString() + _SecondPart.ToString();
SqlDataReader _Reader = null;
try
{
_Connection.Open();
_Reader = _Command.ExecuteReader();
while (_Reader.Read())
{
_Combinations.Add(_Reader.GetString(0));
}
}
catch
{
throw;
}
finally
{
if (_Reader != null && !_Reader.IsClosed)
{
_Reader.Close();
}
if (_Connection != null && _Connection.State != ConnectionState.Closed)
{
_Connection.Close();
}
}
return _Combinations;
}
}
And the SQL to create it is:
CREATE ASSEMBLY [GetCombinations]
AUTHORIZATION [dbo]
FROM 'C:\path\to\DLL'
GO
CREATE FUNCTION [dbo].[GetCombinations]
(#DashedThingy NVARCHAR (MAX))
RETURNS TABLE ([Combo] NVARCHAR (500) NULL)
AS EXTERNAL NAME [GetCombinations].[TVF].[GetCombinations];
This is fully dynamic so it can handle any number of dashes. It finds the missing combination that is not in the example output (A-BC-D) and handles the other example easily:
SELECT * FROM dbo.GetCombinations('XX-W2-ZZZ-AAA-777-888');
Returns:
Combo
---------
XXW2ZZZAAA777888
XXW2-ZZZAAA777888
XXW2ZZZ-AAA777888
XXW2-ZZZ-AAA777888
XXW2ZZZAAA-777888
XXW2-ZZZAAA-777888
XXW2ZZZ-AAA-777888
XXW2-ZZZ-AAA-777888
XX-W2ZZZAAA777888
XX-W2-ZZZAAA777888
XX-W2ZZZ-AAA777888
XX-W2-ZZZ-AAA777888
XX-W2ZZZAAA-777888
XX-W2-ZZZAAA-777888
XX-W2ZZZ-AAA-777888
XX-W2-ZZZ-AAA-777888
XXW2ZZZAAA777-888
XXW2-ZZZAAA777-888
XXW2ZZZ-AAA777-888
XXW2-ZZZ-AAA777-888
XXW2ZZZAAA-777-888
XXW2-ZZZAAA-777-888
XXW2ZZZ-AAA-777-888
XXW2-ZZZ-AAA-777-888
XX-W2ZZZAAA777-888
XX-W2-ZZZAAA777-888
XX-W2ZZZ-AAA777-888
XX-W2-ZZZ-AAA777-888
XX-W2ZZZAAA-777-888
XX-W2-ZZZAAA-777-888
XX-W2ZZZ-AAA-777-888
XX-W2-ZZZ-AAA-777-888

This is a method I found to work inside MSSQL and accept any count of dashes. Main table-value function:
-- =============================================
-- Description: Outputs all possible combinations of dash and no dash in a string
-- Test: Select * From dbo.[fnDashCombinations]('A')
-- Test: Select * From dbo.[fnDashCombinations]('A-B')
-- Test: Select * From dbo.[fnDashCombinations]('AB-CD-EF-11-22')
-- =============================================
ALTER FUNCTION [dbo].[fnDashCombinations]
(
#InputText VARCHAR(50)
)
RETURNS #output TABLE(ModelName VARCHAR(50))
BEGIN
--Get the count of dashes
DECLARE #DashCount INT
SET #DashCount = (select len(#InputText) - len(replace(#InputText, '-', '')))
--Count through the dashes
DECLARE #OuterIterator INT
DECLARE #InnerIterator INT
DECLARE #OuterIteratorLimit INT
DECLARE #DashesAsBinary VARCHAR(50)
DECLARE #DashLocation INT
DECLARE #TextTemp VARCHAR(50)
SET #OuterIteratorLimit = POWER(2, #DashCount) - 1
SET #OuterIterator = 0
WHILE #OuterIterator < #OuterIteratorLimit
BEGIN
--Convert the dash count into the equivalent binary string
SET #TextTemp = #InputText
SET #DashesAsBinary = dbo.fnBinaryString(#OuterIterator, #DashCount)
SET #DashLocation = 0
SET #InnerIterator = 0
--Loop thru #DashesAsBinary and remove the dash if there's a zero
WHILE #InnerIterator < #DashCount
BEGIN
SET #DashLocation = CHARINDEX('-', #TextTemp, #DashLocation + 1)
IF SUBSTRING(#DashesAsBinary, #InnerIterator + 1, 1) = '0'
BEGIN --Replace with underscore for now to keep string length constant
SET #TextTemp = dbo.fnReplaceCharAtPos(#TextTemp, #DashLocation, '_')
END
SET #InnerIterator = #InnerIterator + 1
END
INSERT INTO #output SELECT REPLACE(#TextTemp, '_', '') --Finally remove extra chars
SET #OuterIterator = #OuterIterator + 1
END
RETURN
END
Additional scalar function that is called to convert the iterator into a binary string, like 3 = '011' (found most of this code elsewhere):
ALTER FUNCTION [dbo].[fnBinaryString] (#IncomingNumber int, #MinChars int)
RETURNS varchar(200)
as
BEGIN
DECLARE #BinNumber VARCHAR(200)
SET #BinNumber = ''
WHILE #IncomingNumber <> 0
BEGIN
SET #BinNumber = SUBSTRING('0123456789', (#IncomingNumber % 2) + 1, 1) + #BinNumber
SET #IncomingNumber = #IncomingNumber / 2
END
if (LEN(#BinNumber) < #MinChars)
SET #BinNumber = REPLICATE('0', #MinChars - LEN(#BinNumber)) + #BinNumber
RETURN #BinNumber
END
Additional scalar function that is called to replace a character at a position, used in this case to replace a dash with an underscore (found this code elsewhere):
ALTER FUNCTION [dbo].[fnReplaceCharAtPos](#Str varchar(8000),#Pos int, #Chr char(1))
RETURNS varchar(8000) AS
BEGIN
declare #Res varchar(8000)
set #Res=left(#Str,#Pos-1) + #Chr + right(#Str,len(#Str)-#Pos)
return #Res
END

Related

union the table based on row value

Below is my table structure.
declare #t1 table (id int identity,val int not null,datatype1 int null,datatype2 int null,datatype3 int null,datatype4 int null,type int)
declare #t2 table (id int identity,val int not null,datatype1 int null,datatype2 int null,datatype3 int null,datatype4 int null,type int)
insert into #t1 values (10,1,0,0,0,1),(31,1,0,0,0,1),(20,1,0,0,0,1),(30,1,0,0,0,1)
insert into #t2 values (31,0,1,0,0, 2),(4,0,0,1,0,3),(12,0,0,0,1,4),(31,0,0,0,1,4)
select * from #t1;
select * from #t2;
i am combining 2 table data with below query.
select val,max(datatype1) datatype1,max(datatype2)datatype2,max(datatype3)datatype3,max(datatype4)datatype4 from (
select * from #t1
union all
select * from #t2
) as data group by val
i need to change the logic if val is 31 and type=2 in #t2 ,
for these cases i need get 2 rows for val 31 and other cases only distinct values
Expected Result:
val datatype1 datatype2 datatype3 datatype4
4 0 0 1 0
10 1 0 0 0
12 0 0 0 1
20 1 0 0 0
30 1 0 0 0
31 1 0 0 1
31 0 1 0 0 --- only if in #t2 val =31 and type=2
pls let me knwwat need to be changed for only value 31 and type=2
Based on provided data looks like CASE expression is the way to go:
select val,
max(datatype1) datatype1,
max(datatype2) datatype2,
max(datatype3) datatype3,
max(datatype4) datatype4
from (
select 't1' AS tab_name, * from #t1
union all
select 't2' AS tab_name, * from #t2
) as data
group by val, CASE WHEN tab_name = 't2' and val=31 and type=2 THEN 1 END;
-- creating subgroup for this specific conditions
db<>fiddle demo

Adding two dynamic columns in the table through stored procedure and these columns must have data based on condition

I am trying to add two dynamic columns HeaderText and IsShowHeader to my table through a stored procedure.
In the first column, the first row must have text as Header1 and after 8 rows text must be Header2, then again after 8 rows text must be Header3 and so on.
In the second column value must be 1, and next 8 rows must have 0, the 9th row value must be 1 again, then the next 8 rows must have 0 like this...
ALTER PROCEDURE [dbo].[SkipRow]
AS
BEGIN
SELECT RID
,FirstName
,LastName
,(CASE WHEN X.[Row#]%9=0 And [X].[Row#]=0 THEN 1 ELSE
0 END)As IsShowHeader
,(COUNT(*) OVER ()) as TotalRows FROM
(
SELECT
*,ROW_NUMBER() OVER(ORDER BY RID) AS [Row#]
FROM Mytable1 WITH(NOLOCK)
)X
End
Output:
HeaderText IsShowHeader
1 Header1 1
2 Null 0
3 Null 0
4 Null 0
5 Null 0
6 Null 0
7 Null 0
8 Null 0
9 Null 0
10 Header2 1
11 Null 0
12 Null 0
13 Null 0
14 Null 0
15 Null 0
16 Null 0
17 Null 0
18 Null 0
19 Header3 1
you already have the [Row#], use the modulus operator % to get every 9 rows
HeaderText = case when ([Row#] - 1) % 9 = 0
then 'Header' + convert(varchar(10), ([Row#] - 1) / 9 + 1)
end,
IsShowHeader = case when ([Row#] - 1) % 9 = 0
then 1 else 0 end
You can try this:
SELECT M.id,
M.HeaderText,
CASE WHEN M.HeaderText IS NOT NULL THEN 1 ELSE 0 END AS IsShowHeader
FROM
(
SELECT P.id,
CASE
WHEN P.HeaderText IS NOT NULL THEN
P.HeaderText + CAST(P.IndexNumber AS VARCHAR(10))
ELSE
NULL
END AS HeaderText
FROM
(
SELECT K.id,
HeaderText,
COUNT(K.HeaderText) OVER (ORDER BY id ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS IndexNumber
FROM
(
SELECT id,
CASE
WHEN (id - 1) % 9 = 0 THEN 'Header' ELSE NULL
END AS HeaderText
FROM dbo.test
) AS K
) AS P
) AS M;
I have illustrate the scenario step by step, however you can rewrite it in a simple way like the answer that #Squirrel posted.

Query to identify contiguous ranges

I'm trying to write a query on the below data set to add a new column which has some sort of "period_id_group".
contiguous new_period row_nr new_period_starting_id
0 0 1 0
1 1 2 2
1 0 3 0
1 0 4 0
1 1 5 5
1 0 6 0
What I'm trying to get is:
contiguous new_period row_nr new_period_starting_id period_id_group
0 0 1 0 0
1 1 2 2 2
1 0 3 0 2
1 0 4 0 2
1 1 5 5 5
1 0 6 0 5
The logic is that for each 0 value in the new_period_starting_id, it has to get the >0 value from the row above.
So, for row_nr = 1 since there is no row before it, period_id_group is 0.
For row_nr = 2 since this is a new perid (marked by new_period = 1), the period_id_group is 2 (the id of this row).
For row_nr = 3 since it's part of a contiguous range (because contiguous = 1), but is not the start of the range, because it's not a new_period (new_period = 0), its period_id_group should inherit the value from the previous row (which is the start of the contiguous range) - in this case period_id_group = 2 also.
I've tried multiple versions but couldn't get a good solution for SQL Server 2008R2, since I can't use LAG().
What I have, so far, is a shameful:
select *
from #temp2 t1
left join (select distinct new_period_starting_id from #temp2) t2
on t1.new_period_starting_id >= t2.new_period_starting_id
where 1 = case
when contiguous = 0
then 1
when contiguous = 1 and t2.new_period_starting_id > 0
then 1
else 1
end
order by t1.rn
Sample data script:
declare #tmp2 table (contiguous int
, new_period int
, row_nr int
, new_period_starting_id int);
insert into #tmp2 values (0, 0, 1, 0)
, (1, 1, 2, 2)
, (1, 0, 3, 0)
, (1, 0, 4, 0)
, (1, 1, 5, 5)
, (1, 0, 6, 0);
Any help is appreciated.
So, if I'm understanding you correctly, you just need one additional column.
SELECT t1.contiguous, t1.new_period, t1.row_nr, t1.new_period_starting_id,
(SELECT TOP 1 (new_period_starting_id)
FROM YourTable t2
WHERE t2.row_nr <= t1.row_nr
AND t2.period_id_group > 0 /* optimization */
ORDER BY t2.row_nr DESC /* optimization */) AS period_id_group
FROM YourTable t1
Here is yet another option for this.
select t1.contiguous
, t1.new_period
, t1.row_nr
, t1.new_period_starting_id
, x.new_period_starting_id
from #tmp2 t1
outer apply
(
select top 1 *
from #tmp2 t2
where (t2.row_nr = 1
or t2.new_period_starting_id > 0)
and t1.row_nr >= t2.row_nr
order by t2.row_nr desc
) x
Found the solution:
select *
, case
when contiguous = 0
then f1
when contiguous = 1 and new_periods = 1
then f1
when contiguous = 1 and new_periods = 0
then v
else NULL
end [period_group]
from (
select *
, (select max(f1) from #temp2 where new_period_starting_id > 0 and rn < t1.rn) [v]
from #temp2 t1
) rs
order by rn

Formulating Code and SQL Query

I have a Table in DB named 'Retail'
CustomerID Itemset
1 1
1 3
1 7
2 6
2 7
3 4
... ...
I want to write this table in Datatable 'Matrix' where the Rows are Itemset={1,2,3,4,5,6....,k} and Columns are CustomerID={1,2,3,4,...,x}
and the rows are 1 if the 'Itemset' belongs to the CustomerID.
The output I want is like..
1 2 3 4 5 6 7 ............. x
1 1 0 1 0 0 0 1 0 0 0 0 0 0 0 0
2 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0
3 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0
4
5
6 /And so on!
7
8
9
`
I tried to code it but the problem is in VS Parameters for SQL Query don't work with Loops.
This is my code-
objConnection.Open()
Dim matrix As DataTable = New DataTable("Retail")
intcount = 0
For intcount = 1 To noofCustomerID_col
matrix.Columns.Add(intcount, GetType(Integer))
Next
Dim i As Integer
Dim workRow As DataRow
Dim Boolin As Boolean
For i = 1 To 9
ObjCommand.CommandText = "Select count(*) from Retail Where CustomerID=#in and Itemset=#in"
ObjCommand.Parameters.AddWithValue("#in", i) {{I Get Error here as I can't Loop Parameters}}
Boolin = ObjCommand.ExecuteScalar()
workRow = matrix.NewRow()
workRow(0) = i
workRow(1) = Boolin
matrix.Rows.Add(workRow)
Next
Kindly Help. I know this code can completely be wrong and it's okay if you can suggest totally different way of doing it. I've been stuck for quite some time now! Thanks.
If any clarification is needed I shall explain as many times needed in the comments.
Like Tab Alleman suggested, SQL PIVOT can come handy here.
Dynamic SQL is also needed.
DECLARE #DynamicColumn NVARCHAR(MAX),
#DynamicQuery NVARCHAR(MAX)
SELECT #DynamicColumn = SELECT '[' + CONVERT(NVARCHAR, CustomerID) + '],'
FROM Retail AS Customer
ORDER BY CustomerID
FOR XML PATH('')
SELECT #DynamicColumn = SUBSTRING(#DynamicColumn, 0, DATALENGTH(#DynamicColumn) / 2)--REMOVE EXTRA ","
SELECT #DynamicQuery = 'SELECT *
FROM ( SELECT CustomerID,
ItemSet
FROM Retail) AS D
PIVOT
(
COUNT([CustomerID])
FOR Itemset IN (' + #DynamicQuery + ')
) AS P'
EXECUTE(#DynamicQuery)

mssql query for checking whether the sum is even, odd or zero?

I need to get:
0 if sum(apples) is 0
1 if sum(apples) is odd
2 if sum(apples) is
even
how do i write such a query ?
select answer =
case sum(apples)%2
when 1 then 1
else 2
end
from fruits
I need a third option 0 if the sum is zero ...
My problem is that sum(apples)%2 returns 0 both in even and zero cases.
Thanks
You need to change your query to the following way.
select answer =
case
WHEN sum(apples)=0 then 0
WHEN sum(apples)%2 = 1 then 1
else 2
end
from fruits
as per MySQL Documentation you have two option for syntax, you just needed to change to second option
CASE case_value
WHEN when_value THEN statement_list
[WHEN when_value THEN statement_list] ...
[ELSE statement_list]
END CASE
or
CASE
WHEN search_condition THEN statement_list
[WHEN search_condition THEN statement_list] ...
[ELSE statement_list
END CASE
You may be able to use this little snippet of code in the way that you want...
declare #n int = 0
select case #n when 0 then 'zero' else (case (#n%2) when 1 then 'odd' else 'even' end) end
try it with other values for #n.
Depending on how apples column is defined (it's mandatory or it allows NULLs) SUM could return a non-NULL value or it could return NULL:
SELECT x.OrderID,
SUM(x.Qty) AS SumOfQty,
CASE
WHEN SUM(x.Qty) = 0 THEN 0
WHEN SUM(x.Qty) % 2 = 1 THEN 1
WHEN SUM(x.Qty) % 2 = 0 THEN 2
ELSE -1 -- SUM(x.Qty) IS NULL
END AS CaseSumOfQty
FROM (
SELECT 1, 100 UNION ALL
SELECT 2, 200 UNION ALL
SELECT 2, 301 UNION ALL
SELECT 3, 0 UNION ALL
SELECT 4, NULL
) x(OrderID, Qty)
GROUP BY x.OrderID;
Output:
OrderID SumOfQty CaseSumOfQty
------- ----------- ------------
1 100 2
2 501 1
3 0 0
4 NULL -1 -- SUM(x.Qty) IS NULL

Resources