Formulating Code and SQL Query - sql-server

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)

Related

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.

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

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

Get Min and Max Dates from SQL Server SQL

Data in my table looks like this
PAY_END_DT Sal
10/27/2013 0
11/10/2013 0
11/24/2013 2473.14
12/08/2013 0
01/19/2014 0
02/02/2014 0
02/16/2014 0
My desired result should be like as below
10/27/2013 11/10/2013
12/08/2013 02/16/2014
I need a SQL to generate this result set.. please help
SELECT
employee_id,
MIN(pay_end_dt) AS island_min_pay_end_dt,
MAX(pay_end_dt) AS island_max_pay_end_dt
FROM
(
SELECT
pay_end_dt,
ROW_NUMBER() OVER (PARTITION BY employee_id,
ORDER BY pay_end_dt ) AS full_set_ordinal,
ROW_NUMBER() OVER (PARTITION BY employee_id, sal
ORDER BY pay_end_dt ) AS zero_set_ordinal
FROM
yourTable
)
AS sorted_set
WHERE
sal = 0
GROUP BY
employee_id,
full_set_ordinal - zero_set_ordinal
;
Using your data as an example:
PAY_END_DT Sal FULL_SET_ORIDINAL ZERO_SET_ORDINAL "FULL - ZERO"
10/27/2013 0 1 1 0
11/10/2013 0 2 2 0
11/24/2013 2473.14 3 1 2
12/08/2013 0 4 3 1
01/19/2014 0 5 4 1
02/02/2014 0 6 5 1
02/16/2014 0 7 6 1
Which then allows us to include only the rows WHERE sal = 0 and then GROUP BY "FULL - ZERO" to get our two sets, and then finally apply the MIN() and MAX() functions.
As stated in a comment, this is known as "Gaps and Islands".
1 1 1 1
0 0 0 0 0
1 2 3 4 5 6 7 8 9 - ordinal from the whole set
1 2 3 4 - ordinal from just the "islands"
1 2 3 4 5 - ordinal from just the "gaps"
2 2 2 3 - ordinal of the "islands" (whole_set_id - islands_id)
0 0 3 4 4 - ordinal of the "gaps" (whole_set_id - gaps_id)
;)
SELECT "10/27/2013 11/10/2013"
UNION
SELECT "12/08/2013 02/16/2014"

Partition data into subgroups based on bit fields

I have the first 4 columns of data, and I wan't to use the Ranking functions in the SQL 2008 R2 to derive the fifth column. What's the best way to partition the data into subgroups based on the nextiteminsubgroup and previousiteminsubgroup fields?
Group OrderInGroup NextItemInSubGroup PreviousItemInSubGroup SubGroup
1 1 1 0 1
1 2 1 1 1
1 3 1 1 1
1 4 0 1 1
1 5 0 0 2
1 6 0 0 3
1 7 1 0 4
1 8 1 1 4
1 9 0 1 4
2 1 0 0 1
2 2 0 0 2
2 3 0 0 3
2 4 1 0 4
2 5 0 1 4
3 1 0 0 1
4 1 0 0 1
4 2 0 0 2
4 3 0 0 3
A recursive CTE solution:
DECLARE #t TABLE
([Group] INT
,OrderInGroup INT
,NextItemInSubGroup INT
,PreviousItemInSubGroup INT
,SubGroup INT
)
INSERT #t
VALUES
(1,1,1,0,1),(1,2,1,1,1),(1,3,1,1,1),(1,4,0,1,1),(1,5,0,0,2),(1,6,0,0,3),
(1,7,1,0,4),(1,8,1,1,4),(1,9,0,1,4),(2,1,0,0,1),(2,2,0,0,2),(2,3,0,0,3),
(2,4,1,0,4),(2,5,0,1,4),(3,1,0,0,1),(4,1,0,0,1),(4,2,0,0,2),(4,3,0,0,3)
;WITH recCTE
AS
(
SELECT [Group], OrderInGroup,NextItemInSubGroup , PreviousItemInSubGroup, 1 AS subgroup
FROM #t
WHERE OrderInGroup = 1
UNION ALL
SELECT r.[Group], t.OrderInGroup,t.NextItemInSubGroup , t.PreviousItemInSubGroup,
CASE WHEN r.NextItemInSubGroup = 1 THEN r.subgroup ELSE r.subgroup + 1 END
FROM recCTE AS r
JOIN #t AS t
ON t.[Group] = r.[Group]
AND t.OrderInGroup = r.OrderInGroup + 1
)
SELECT * FROM recCTE
ORDER BY [Group],OrderInGroup ;
P.S. it's best practice to avoid using SQL keywords (e.g. GROUP) as table/column names
Seems like 0 and 0 restart the ranking.
Select
Rank() Over (
Partition By
[Group]
, Case When [NextItemInSubGroup] + [PreviousItemInSubGroup] = 0
Then 0
Else 1
End
Order By [OrderInGroup]
) as [SubGroup]
From Your_Table;

Combining multiple rows into one row

I have a table containing user-account permissions and I'm trying to write a query to return one row for each user-account combination.
Here is what I have.
CltKey AcctKey TranTypeID Access
10 2499 10 0
10 2499 11 1
10 2499 12 1
10 2764 10 1
10 2764 11 1
10 2764 12 0
Here is what I'd like to have.
CltKey AcctKey TranTypeID1 Access1 TranTypeID2 Access2 TranTypeID3 Access3
10 2499 10 0 11 1 12 1
10 2764 10 1 11 1 12 0
Or even better something like this.
CltKey AcctKey HasTranTypeID1 HasTranTypeID2 HasTranTypeID3
10 2499 0 1 1
10 2764 1 1 0
I have tried doing a self join, but I keep getting multiple rows for each TranTypeID. One with it equal to 0 and another with it equal to 1. I have also tried using nested "Select" statements, but the performance is horrible. Does anyone have an idea on how to do this?
Thanks.
Edit: Unfortunately, this has to work in SQL 2000.
It's been a while since I used SQLServer 2000, but this will probably work.
select cltkey, acctkey,
max( case when trantypeid = 10 and access = 1
then 1 else 0 end ) as hastrantypeid1,
max( case when trantypeid = 11 and access = 1
then 1 else 0 end ) as hastrantypeid2,
max( case when trantypeid = 12 and access = 1
then 1 else 0 end ) as hastrantypeid3
from table
group by cltkey, acctkey;
If not, try this:
create view has_access as
select cltkey, acctkey,
max( case when trantypeid = 10 and access = 1
then 1 else 0 end ) as hastrantypeid1,
max( case when trantypeid = 11 and access = 1
then 1 else 0 end ) as hastrantypeid2,
max( case when trantypeid = 12 and access = 1
then 1 else 0 end ) as hastrantypeid3
from table;
and then get your results from this
select cltkey, acctkey,
max( hastrantypeid1) as hastrantypeid1,
max( hastrantypeid2 ) as hastrantypeid2,
max( hastrantypeid2 ) as hastrantypeid2
from has_access
group by cltkey, acctkey;
Note that this will tell you a (cltkey, acctkey) has access (of a particular type) if any row for that tuple of (cltkey, acctkey) has access for that particular type. That is, it's essentially a row-wise OR.
If all rows for that tuple must have access for that tuple to have access, that is, if you want a row-wise AND, you'll need to do this:
min( case when trantypeid = 10
then case when access = 1
then 1 else 0 end else null end) as hastrantypeid1,
etc.
SELECT CltKey, AcctKey,
MAX(CASE TrantypeId WHEN 10 THEN Access ELSE NULL END) AS HasTranTypeID1,
MAX(CASE TrantypeId WHEN 11 THEN Access ELSE NULL END) AS HasTranTypeID2,
MAX(CASE TrantypeId WHEN 12 THEN Access ELSE NULL END) AS HasTranTypeID3
FROM PermissionsTable
GROUP BY CltKey, AcctKey
ORDER BY CltKey, AcctKey
;
Use PIVOT - Here is an example: http://msdn.microsoft.com/en-us/library/ms177410.aspx

Resources