This question is asked many time here but i am not getting the proper output as i am expecting:
I have a table in which i need to concatenate columns but i don't want NULL value in it. I want it in sql server 2008 concate function does not work in 2008.
Example:
OrderTable
Customer_Number order1 order2 order3 order4
1 NULL X Y NULL
2 NULL A B NULL
3 V NULL H NULL
Now want i want is the data in concatenated manner for order only like this:
Customer_Number Order
1 X,Y
2 A,B
3 V,H
This is the code i used
Select Customer_number, ISNULL(NULLIF(order1,' ')+',','')+
ISNULL(NULLIF(order2,' ')+',','')+
ISNULL(NULLIF(order3,' ')+',','')+
ISNULL(NULLIF(order4,' ')+',','')
as Order from Ordertable
I got the below output
Customer_Number Order
1 NULL,X,Y,NULL
2 NULL,A,B,NULL
3 V,NULL,H,NULL
I already try Coalesce, Stuff, ISNULL, NULLIF but all have same result
Thanks in advance !!!
Another variation, for fun and profit, demonstrating the FOR XML trick to concatenate values pre-SQL Server 2012.
SELECT Customer_Number, STUFF(
(SELECT ',' + order1, ',' + order2, ',' + order3, ',' + order4 FOR XML PATH('')),
1, 1, ''
)
This is slight overkill for a constant number of columns (and not particularly efficient), but an easy to remember pattern for concatenation. Also, it shows off STUFF, a function any SQL developer should learn to love.
Example
Declare #YourTable Table ([Customer_Number] varchar(50),[order1] varchar(50),[order2] varchar(50),[order3] varchar(50),[order4] varchar(50))
Insert Into #YourTable Values
(1,NULL,'X','Y',NULL)
,(2,NULL,'A','B',NULL)
,(3,'V',NULL,'H',NULL)
Select Customer_Number
,[Order] = IsNull(stuff(
IsNull(','+order1,'')
+IsNull(','+order2,'')
+IsNull(','+order3,'')
+IsNull(','+order4,'')
,1,1,''),'')
From #YourTable
Returns
Customer_Number Order
1 X,Y
2 A,B
3 V,H
EDIT - IF the "NULL" are strings and NOT NULL Values
Declare #YourTable Table ([Customer_Number] varchar(50),[order1] varchar(50),[order2] varchar(50),[order3] varchar(50),[order4] varchar(50))
Insert Into #YourTable Values
(1,'NULL','X','Y','NULL')
,(2,'NULL','A','B','NULL')
,(3,'V','NULL','H','NULL')
Select Customer_Number
,[Order] = IsNull(stuff(
IsNull(','+nullif(order1,'NULL'),'')
+IsNull(','+nullif(order2,'NULL'),'')
+IsNull(','+nullif(order3,'NULL'),'')
+IsNull(','+nullif(order4,'NULL'),'')
,1,1,''),'')
From #YourTable
This is a bit unpleasant, it needs to be in a subquery to remove the trailing comma efficiently (though you could use a CTE):
SELECT
Customer_number,
SUBSTRING([Order], 0, LEN([Order])) AS [Order]
FROM(
SELECT
Customer_number,
COALESCE(order1+',', '') +
COALESCE(order2+',', '') +
COALESCE(order3+',', '') +
COALESCE(order4+',', '') AS [Order]
FROM
OrderTable) AS SubQuery
You were on the right track with the Coalesce, or IsNull. Instead of trying to track the length and use substring, left or stuff, I just used a replace to remove the trailing comma that would show up from the coalesce.
Select Customer_number,
Replace(
Coalesce(Order1 + ',','')+
Coalesce(Order2 + ',','')+
Coalesce(Order3 + ',','')+
Coalesce(Order4 + ',','')
+',',',,','') --Hack to remove the Last Comma
as [Order] from Ordertable
Related
I have a field which is a concatenation of single letters. I am trying to order these strings within a view. These values can't be hard coded as there are too many. Is someone able to provide some guidance on the function to use to achieve the desired output below? I am using MSSQL.
Current output
CustID | Code
123 | BCA
Desired output
CustID | Code
123 | ABC
I have tried using a UDF
CREATE FUNCTION [dbo].[Alphaorder] (#str VARCHAR(50))
returns VARCHAR(50)
BEGIN
DECLARE #len INT,
#cnt INT =1,
#str1 VARCHAR(50)='',
#output VARCHAR(50)=''
SELECT #len = Len(#str)
WHILE #cnt <= #len
BEGIN
SELECT #str1 += Substring(#str, #cnt, 1) + ','
SET #cnt+=1
END
SELECT #str1 = LEFT(#str1, Len(#str1) - 1)
SELECT #output += Sp_data
FROM (SELECT Split.a.value('.', 'VARCHAR(100)') Sp_data
FROM (SELECT Cast ('<M>' + Replace(#str1, ',', '</M><M>') + '</M>' AS XML) AS Data) AS A
CROSS APPLY Data.nodes ('/M') AS Split(a)) A
ORDER BY Sp_data
RETURN #output
END
This works when calling one field
ie.
Select CustID, dbo.alphaorder(Code)
from dbo.source
where custid = 123
however when i try to apply this to top(10) i receive the error
"Invalid length parameter passed to the LEFT or SUBSTRING function."
Keeping in mind my source has ~4million records, is this still the best solution?
Unfortunately i am not able to normalize the data into a separate table with records for each Code.
This doesn't rely on a id column to join with itself, performance is almost as fast
as the answer by #Shnugo:
SELECT
CustID,
(
SELECT
chr
FROM
(SELECT TOP(LEN(Code))
SUBSTRING(Code,ROW_NUMBER() OVER(ORDER BY (SELECT NULL)),1)
FROM sys.messages) A(Chr)
ORDER by chr
FOR XML PATH(''), type).value('.', 'varchar(max)'
) As CODE
FROM
source t
First of all: Avoid loops...
You can try this:
DECLARE #tbl TABLE(ID INT IDENTITY, YourString VARCHAR(100));
INSERT INTO #tbl VALUES ('ABC')
,('JSKEzXO')
,('QKEvYUJMKRC');
--the cte will create a list of all your strings separated in single characters.
--You can check the output with a simple SELECT * FROM SeparatedCharacters instead of the actual SELECT
WITH SeparatedCharacters AS
(
SELECT *
FROM #tbl
CROSS APPLY
(SELECT TOP(LEN(YourString)) ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) FROM master..spt_values) A(Nmbr)
CROSS APPLY
(SELECT SUBSTRING(YourString,Nmbr,1))B(Chr)
)
SELECT ID,YourString
,(
SELECT Chr As [*]
FROM SeparatedCharacters sc1
WHERE sc1.ID=t.ID
ORDER BY sc1.Chr
FOR XML PATH(''),TYPE
).value('.','nvarchar(max)') AS Sorted
FROM #tbl t;
The result
ID YourString Sorted
1 ABC ABC
2 JSKEzXO EJKOSXz
3 QKEvYUJMKRC CEJKKMQRUvY
The idea in short
The trick is the first CROSS APPLY. This will create a tally on-the-fly. You will get a resultset with numbers from 1 to n where n is the length of the current string.
The second apply uses this number to get each character one-by-one using SUBSTRING().
The outer SELECT calls from the orginal table, which means one-row-per-ID and use a correalted sub-query to fetch all related characters. They will be sorted and re-concatenated using FOR XML. You might add DISTINCT in order to avoid repeating characters.
That's it :-)
Hint: SQL-Server 2017+
With version v2017 there's the new function STRING_AGG(). This would make the re-concatenation very easy:
WITH SeparatedCharacters AS
(
SELECT *
FROM #tbl
CROSS APPLY
(SELECT TOP(LEN(YourString)) ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) FROM master..spt_values) A(Nmbr)
CROSS APPLY
(SELECT SUBSTRING(YourString,Nmbr,1))B(Chr)
)
SELECT ID,YourString
,STRING_AGG(sc.Chr,'') WITHIN GROUP(ORDER BY sc.Chr) AS Sorted
FROM SeparatedCharacters sc
GROUP BY ID,YourString;
Considering your table having good amount of rows (~4 Million), I would suggest you to create a persisted calculated field in the table, to store these values. As calculating these values at run time in a view, will lead to performance problems.
If you are not able to normalize, add this as a denormalized column to the existing table.
I think the error you are getting could be due to empty codes.
If LEN(#str) = 0
BEGIN
SET #output = ''
END
ELSE
BEGIN
... EXISTING CODE BLOCK ...
END
I can suggest to split string into its characters using referred SQL function.
Then you can concatenate string back, this time ordered alphabetically.
Are you using SQL Server 2017? Because with SQL Server 2017, you can use SQL String_Agg string aggregation function to concatenate characters splitted in an ordered way as follows
select
t.CustId, string_agg(strval, '') within GROUP (order by strval)
from CharacterTable t
cross apply dbo.SPLIT(t.code) s
where strval is not null
group by CustId
order by CustId
If you are not working on SQL2017, then you can follow below structure using SQL XML PATH for concatenation in SQL
select
CustId,
STUFF(
(
SELECT
'' + strval
from CharacterTable ct
cross apply dbo.SPLIT(t.code) s
where strval is not null
and t.CustId = ct.CustId
order by strval
FOR XML PATH('')
), 1, 0, ''
) As concatenated_string
from CharacterTable t
order by CustId
I have two columns in a table. I want to merge them into a single column, but the merge should be done taking alternate characters from each columns.
For example:
Column A --> value (1,2,3)
Column B --> value (A,B,C)
Required result - (1,A,2,B,3,C)
It should be done without loops.
You need to make use of the UNION and get a little creative with how you choose to alternate. My solution ended up looking like this.
SELECT ColumnA
FROM Table
WHERE ColumnA%2=1
UNION
SELECT ColumnB
FROM TABLE
WHERE ColumnA%2=0
If you have an ID/PK column that could just as easily be used, I just didn't want to assume anything about your table.
EDIT:
If your table contains duplicates that you wish to keep, use UNION ALL instead of UNION
Try This;
SELECT [value]
FROM [Table]
UNPIVOT
(
[value] FOR [Column] IN ([Column_A], [Column_B])
) UNPVT
If you have SQL 2016 or higher you can use:
SELECT QUOTENAME(STRING_AGG (cast(a as varchar(1)) + ',' + b, ','), '()')
FROM test;
In older versions, depending on how much data you have in your tables you can also try:
SELECT QUOTENAME(STUFF(
(SELECT ',' + cast(a as varchar(1)) + ',' + b
FROM test
FOR XML PATH('')), 1, 1,''), '()')
Here you can try a sample
http://sqlfiddle.com/#!18/6c9af/5
with data as (
select *, row_number() over order by colA) as rn
from t
)
select rn,
case rn % 2 when 1 then colA else colB end as alternating
from data;
The following SQL uses undocumented aggregate concatenation technique. This is described in Inside Microsoft SQL Server 2008 T-SQL Programming on page 33.
declare #x varchar(max) = '';
declare #t table (a varchar(10), b varchar(10));
insert into #t values (1,'A'), (2,'B'),(3,'C');
select #x = #x + a + ',' + b + ','
from #t;
select '(' + LEFT(#x, LEN(#x) - 1) + ')';
How can I take a SQL table that looks like this:
MemberNumber JoinDate Associate
1234 1/1/2011 A1 free A2 upgrade A31
5678 3/15/2011 A4
9012 5/10/2011 free
And output (using a view or writing to another table or whatever is easiest) this:
MemberNumber Date
1234-P 1/1/2011
1234-A1 1/1/2011
1234-A2 1/1/2011
1234-A31 1/1/2011
5678-P 3/15/2011
5678-A4 3/15/2011
9012-P 5/10/2011
Where each row results in a "-P" (primary) output line as well as any A# (associate) lines. The Associate field can contain a number of different non-"A#" values, but the "A#"s are all I'm interested in (# is from 1 to 99). There can be many "A#"s in that one field too.
Of course a table redesign would greatly simplify this query but sometimes we just need to get it done. I wrote the below query using multiple CTEs; I find its easier to follow and see exactly whats going on, but you could simplify this further once you grasp the technique.
To inject your "P" primary row you will see that I simply jammed it into Associate column but it might be better placed in a simple UNION outside the CTEs.
In addition, if you do choose to refactor your schema the below technique can be used to "split" your Associate column into rows.
;with
Split (MemberNumber, JoinDate, AssociateItem)
as ( select MemberNumber, JoinDate, p.n.value('(./text())[1]','varchar(25)')
from ( select MemberNumber, JoinDate, n=cast('<n>'+replace(Associate + ' P',' ','</n><n>')+'</n>' as xml).query('.')
from #t
) a
cross apply n.nodes('n') p(n)
)
select MemberNumber + '-' + AssociateItem,
JoinDate
from Split
where left(AssociateItem, 1) in ('A','P')
order
by MemberNumber;
The XML method is not a great option performance-wise, as its speed degrades as the number of items in the "array" increases. If you have long arrays the follow approach might be of use to you:
--* should be physical table, but use this cte if needed
--;with
--number (n)
--as ( select top(50) row_number() over(order by number) as n
-- from master..spt_values
-- )
select MemberNumber + '-' + substring(Associate, n, isnull(nullif(charindex(' ', Associate + ' P', n)-1, -1), len(Associate)) - n+1),
JoinDate
from ( select MemberNumber, JoinDate, Associate + ' P' from #t
) t (MemberNumber, JoinDate, Associate)
cross
apply number n
where n <= convert(int, len(Associate)) and
substring(' ' + Associate, n, 1) = ' ' and
left(substring(Associate, n, isnull(nullif(charindex(' ', Associate, n)-1, -1), len(Associate)) - n+1), 1) in ('A', 'P');
Try this new version
declare #t table (MemberNumber varchar(8), JoinDate date, Associate varchar(50))
insert into #t values ('1234', '1/1/2011', 'A1 free A2 upgrade A31'),('5678', '3/15/2011', 'A4'),('9012', '5/10/2011', 'free')
;with b(f, t, membernumber, joindate, associate)
as
(
select 1, 0, membernumber, joindate, Associate
from #t
union all
select t+1, charindex(' ',Associate + ' ', t+1), membernumber, joindate, Associate
from b
where t < len(Associate)
)
select MemberNumber + case when t = 0 then '-P' else '-'+substring(Associate, f,t-f) end NewMemberNumber, JoinDate
from b
where t = 0 or substring(Associate, f,1) = 'A'
--where t = 0 or substring(Associate, f,2) like 'A[1-9]'
-- order by MemberNumber, t
Result is the same as the requested output.
I would recommend altering your database structure by adding a link table instead of the "Associate" column. A link table would consist of two or more columns like this:
MemberNumber Associate Details
-----------------------------------
1234 A1 free
1234 A2 upgrade
1234 A31
5678 A4
Then the desired result can be obtained with a simple JOIN:
SELECT CONCAT(m.`MemberNumber`, '-', 'P'), m.`JoinDate`
FROM `members` m
UNION
SELECT CONCAT(m.`MemberNumber`, '-', IFNULL(a.`Associate`, 'P')), m.`JoinDate`
FROM `members` m
RIGHT JOIN `members_associates` a ON m.`MemberNumber` = a.`MemberNumber`
Maybe I demand too much from SQL but I feel like this should be possible. I start with a list of key-value pairs, like this:
'0:First, 1:Second, 2:Third, 3:Fourth'
etc. I can split this up pretty easily with a two-step parse that gets me a table like:
EntryNumber PairNumber Item
0 0 0
1 0 First
2 1 1
3 1 Second
etc.
Now, in the simple case of splitting the pairs into a pair of columns, it's fairly easy. I'm interested in the more advanced case where I might have multiple values per entry, like:
'0:First:Fishing, 1:Second:Camping, 2:Third:Hiking'
and such.
In that generic case, I'd like to find a way to take my 3-column result table and somehow pivot it to have one row per entry and one column per value-part.
So I want to turn this:
EntryNumber PairNumber Item
0 0 0
1 0 First
2 0 Fishing
3 1 1
4 1 Second
5 1 Camping
Into this:
Entry [1] [2] [3]
0 0 First Fishing
1 1 Second Camping
Is that just too much for SQL to handle, or is there a way? Pivots (even tricky dynamic pivots) seem like an answer, but I can't figure how to get that to work.
No, in SQL you can't infer columns dynamically based on the data found during the same query.
Even using the PIVOT feature in Microsoft SQL Server, you must know the columns when you write the query, and you have to hard-code them.
You have to do a lot of work to avoid storing the data in a relational normal form.
Alright, I found a way to accomplish what I was after. Strap in, this is going to get bumpy.
So the basic problem is to take a string with two kinds of delimiters: entries and values. Each entry represents a set of values, and I wanted to turn the string into a table with one column for each value per entry. I tried to make this a UDF, but the necessity for a temporary table and dynamic SQL meant it had to be a stored procedure.
CREATE PROCEDURE [dbo].[ParseValueList]
(
#parseString varchar(8000),
#itemDelimiter CHAR(1),
#valueDelimiter CHAR(1)
)
AS
BEGIN
SET NOCOUNT ON;
IF object_id('tempdb..#ParsedValues') IS NOT NULL
BEGIN
DROP TABLE #ParsedValues
END
CREATE TABLE #ParsedValues
(
EntryID int,
[Rank] int,
Pair varchar(200)
)
So that's just basic set up, establishing the temp table to hold my intermediate results.
;WITH
E1(N) AS (SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1),--Brute forces 10 rows
E2(N) AS (SELECT 1 FROM E1 a, E1 b), --Uses a cross join to generate 100 rows (10 * 10)
E4(N) AS (SELECT 1 FROM E2 a, E2 b), --Uses a cross join to generate 10,000 rows (100 * 100)
cteTally(N) AS (SELECT ROW_NUMBER() OVER (ORDER BY N) FROM E4)
That beautiful piece of SQL comes from SQL Server Central's Forums and is credited to "a guru." It's a great little 10,000 line tally table perfect for string splitting.
INSERT INTO #ParsedValues
SELECT ItemNumber AS EntryID, ROW_NUMBER() OVER (PARTITION BY ItemNumber ORDER BY ItemNumber) AS [Rank],
SUBSTRING(Items.Item, T1.N, CHARINDEX(#valueDelimiter, Items.Item + #valueDelimiter, T1.N) - T1.N) AS [Value]
FROM(
SELECT ROW_NUMBER() OVER (ORDER BY T2.N) AS ItemNumber,
SUBSTRING(#parseString, T2.N, CHARINDEX(#itemDelimiter, #parseString + #itemDelimiter, T2.N) - T2.N) AS Item
FROM cteTally T2
WHERE T2.N < LEN(#parseString) + 2 --Ensures we cut out once the entire string is done
AND SUBSTRING(#itemDelimiter + #parseString, T2.N, 1) = #itemDelimiter
) AS Items, cteTally T1
WHERE T1.N < LEN(#parseString) + 2 --Ensures we cut out once the entire string is done
AND SUBSTRING(#valueDelimiter + Items.Item, T1.N, 1) = #valueDelimiter
Ok, this is the first really dense meaty part. The inner select is breaking up my string along the item delimiter (the comma), using the guru's string splitting method. Then that table is passed up to the outer select which does the same thing, but this time using the value delimiter (the colon) to each row. The inner RowNumber (EntryID) and the outer RowNumber over Partition (Rank) are key to the pivot. EntryID show which Item the values belong to, and Rank shows the ordinal of the values.
DECLARE #columns varchar(200)
DECLARE #columnNames varchar(2000)
DECLARE #query varchar(8000)
SELECT #columns = COALESCE(#columns + ',[' + CAST([Rank] AS varchar) + ']', '[' + CAST([Rank] AS varchar)+ ']'),
#columnNames = COALESCE(#columnNames + ',[' + CAST([Rank] AS varchar) + '] AS Value' + CAST([Rank] AS varchar)
, '[' + CAST([Rank] AS varchar)+ '] AS Value' + CAST([Rank] AS varchar))
FROM (SELECT DISTINCT [Rank] FROM #ParsedValues) AS Ranks
SET #query = '
SELECT '+ #columnNames +'
FROM #ParsedValues
PIVOT
(
MAX([Value]) FOR [Rank]
IN (' + #columns + ')
) AS pvt'
EXECUTE(#query)
DROP TABLE #ParsedValues
END
And at last, the dynamic sql that makes it possible. By getting a list of Distinct Ranks, we set up our column list. This is then written into the dynamic pivot which tilts the values over and slots each value into the proper column, each with a generic "Value#" heading.
Thus by calling EXEC ParseValueList with a properly formatted string of values, we can break it up into a table to feed into our purposes! It works (but is probably overkill) for simple key:value pairs, and scales up to a fair number of columns (About 50 at most, I think, but that'd be really silly.)
Anyway, hope that helps anyone having a similar issue.
(Yeah, it probably could have been done in something like SQLCLR as well, but I find a great joy in solving problems with pure SQL.)
Though probably not optimal, here's a more condensed solution.
DECLARE #DATA varchar(max);
SET #DATA = '0:First:Fishing, 1:Second:Camping, 2:Third:Hiking';
SELECT
DENSE_RANK() OVER (ORDER BY [Data].[row]) AS [Entry]
, [Data].[row].value('(./B/text())[1]', 'int') as "[1]"
, [Data].[row].value('(./B/text())[2]', 'varchar(64)') as "[2]"
, [Data].[row].value('(./B/text())[3]', 'varchar(64)') as "[3]"
FROM
(
SELECT
CONVERT(XML, '<A><B>' + REPLACE(REPLACE(#DATA , ',', '</B></A><A><B>'), ':', '</B><B>') + '</B></A>').query('.')
) AS [T]([c])
CROSS APPLY [T].[c].nodes('/A') AS [Data]([row]);
Hope is not too late.
You can use the function RANK to know the position of each Item per PairNumber. And then use Pivot
SELECT PairNumber, [1] ,[2] ,[3]
FROM
(
SELECT PairNumber, Item, RANK() OVER (PARTITION BY PairNumber order by EntryNumber) as RANKing
from tabla) T
PIVOT
(MAX(Item)
FOR RANKing in ([1],[2],[3])
)as PVT
I need a solution to a problem that has the table structure as listed below.
Input
1 1/1/2009 Product1
2 2/2/2009 Product2
3 3/3/2009 Product3
4 4/4/2009 Product4
5 5/5/2009 Product5
Output
1 1/1/2009 2/2009 Product1
2 3/3/2009 4/4/2009 Product3
3 5/5/2009 Product5
I tried using CTE. But was not very sucessful in extracting the second row value.
Appreciate any help. Thanks.
I don't know whether Russ' answer helps you at all. Here is a link to an article that explains how to add row numbers to the results of a query. (Search for "row_number" to find the most likely example.)
Once you have a query numbering the rows properly, you should be able to throw that into a CTE, then select from it twice -- once for odd numbers, then again for even numbers. Have each result return the even numbered value for joining (odd - 1 = even). At that point, you can join the results of the queries and get two products on one row.
You are looking for PIVOT: http://msdn.microsoft.com/en-us/library/ms177410.aspx
Here is a best shot with the info you gave, I do something similar in one of my apps. You may need to use a dynamic SQL query if the pivot values change.
SELECT *
FROM (SELECT [Date]
,[Product]
FROM [Values]
PIVOT (Max([Date])
FOR [Product]
IN ('put date ranges here')) pvt
here is what mine looks like, this will allow for a set of different values. This is used in a form builder to retrive the values of user input
--//Get a comma delimited list of field names from the field table for this form
DECLARE #FieldNames varchar(max)
SELECT #FieldNames = COALESCE(#FieldNames + ', ', '') + '[' + CAST([FieldName] AS varchar(max)) + ']'
FROM [Fields]
WHERE [FormID] = #FormID
--//create a dynamic sql pivot table that will bring our
--//fields and values together to look like a real sql table
DECLARE #SQL varchar(max)
SET #SQL =
'SELECT *
FROM (SELECT [Values].[RowID]
,[Fields].[FieldName]
,[Values].[Value]
FROM [Values]
INNER JOIN [Fields] ON [Fields].[FieldID] = [Values].[FieldID]
WHERE [Fields].[FormID] = ''' + #FormID + ''') p
PIVOT (Max([Value])
FOR [FieldName]
IN (' + #FieldNames + ')) pvt'
--//execute our sql to return the data
EXEC(#SQL)
You can actually create a dummy column initially and create a cte.
Then, use cte and join them on the dummy key and the row number or a series which simulates sequential numbers.
Then, filter in the dataset to display only odd numbered rows
create table dbo.test
(
id integer,
currdate varchar(20), -- just to keep simple, not to mess with dates, took this as string
productCode varchar(20)
);
insert into test values (1, '1/1/2009', 'product1');
insert into test values (2, '2/2/2009', 'product2');
insert into test values (3, '3/3/2009', 'product3');
insert into test values (4, '4/4/2009', 'product4');
insert into test values (5, '5/5/2009', 'product5');
with ctes as
(
select
't' as joinKey,
id, -- serves as rownum or else create another using row_num() over partition by and order by
currdate,
productCode
from test
)
select
c1.id,
c1.currdate,
c1.productCode,
c2.id,
c2.currdate,
c2.productCode
from
(
select
joinKey,
id,
currdate,
productCode
from
ctes
)c1,
(
select
joinKey,
id,
currdate,
productCode
from
ctes
)c2
where c1.joinKey = c2.joinKey
and c1.id + 1 = c2.id
and c1.id % 2 = 1
The result is as shown below:
id currdate productCode id currdate productCode
1 1/1/2009 product1 2 2/2/2009 product2
3 3/3/2009 product3 4 4/4/2009 product4