how to substring an empty phrase in sql? - sql

I have a link and I want to substring it into many pieces to get numbers
example:-
&OR=-12&MR=24560&MM=20&Mx=110&EOW=1&OW=50&OV=12&CV=-1
I want the -12 , 24560 , 20 , ... etc.
and I did it using this code
DECLARE #strprog AS NVARCHAR(MAX)--,#strLM AS NVARCHAR(20),#strLOW AS NVARCHAR(20),#strLOR AS NVARCHAR(20),#strLCR AS NVARCHAR(20),
DECLARE #OR AS SMALLINT, #MR AS SMALLINT, #MM AS SMALLINT, #Mx AS SMALLINT, #EOW AS SMALLINT, #OW AS SMALLINT, #OV AS SMALLINT, #CV AS SMALLINT
SET #strprog='WorkFlow/WFmain.aspx?sservice=WFAppraisalManage&showrep=1&applyRole=0&DisableApproval=1&grbCompQuest=1&EnablecompletionChk=1
&MaxCompletionNo=110&ProficiencyValidation=0&objresulttxt=0
&OR=-12&MR=24560&MM=20&Mx=110&EOW=1&OW=50&OV=12&CV=-1'
SET #OR= SUBSTRING(#strprog,CHARINDEX('&OR=',#strprog)+4,CASE WHEN CHARINDEX('&',#strprog,CHARINDEX('&OR=',#strprog)+4 ) <=0 THEN LEN(#strprog)+1 ELSE CHARINDEX('&',#strprog,CHARINDEX('&OR=',#strprog)+4 ) END -CHARINDEX('&OR=',#strprog)-4 )
SELECT #OR
SET #MR= SUBSTRING(#strprog,CHARINDEX('&MR=',#strprog)+4,CASE WHEN CHARINDEX('&',#strprog,CHARINDEX('&MR=',#strprog)+4 ) <=0 THEN LEN(#strprog)+1 ELSE CHARINDEX('&',#strprog,CHARINDEX('&MR=',#strprog)+4 ) END -CHARINDEX('&MR=',#strprog)-4 )
SELECT #MR
SET #MM= SUBSTRING(#strprog,CHARINDEX('&MM=',#strprog)+4,CASE WHEN CHARINDEX('&',#strprog,CHARINDEX('&MM=',#strprog)+4 ) <=0 THEN LEN(#strprog)+1 ELSE CHARINDEX('&',#strprog,CHARINDEX('&MM=',#strprog)+4 ) END -CHARINDEX('&MM=',#strprog)-4 )
SELECT #MM
SET #Mx= SUBSTRING(#strprog, CHARINDEX('&Mx=',#strprog)+4 ,CASE WHEN CHARINDEX('&',#strprog,CHARINDEX('&Mx=',#strprog)+4 ) <=0 THEN LEN(#strprog)+1 ELSE CHARINDEX('&',#strprog,CHARINDEX('&Mx=',#strprog)+4 ) END -CHARINDEX('&Mx=',#strprog)-4 )
SELECT #Mx
SET #EOW= SUBSTRING(#strprog,CHARINDEX('&EOW=',#strprog)+5,CASE WHEN CHARINDEX('&',#strprog,CHARINDEX('&EOW=',#strprog)+5 ) <=0 THEN LEN(#strprog)+1 ELSE CHARINDEX('&',#strprog,CHARINDEX('&EOW=',#strprog)+5 ) END -CHARINDEX('&EOW=',#strprog)-5 )
SELECT #EOW
SET #OW= SUBSTRING(#strprog,CHARINDEX('&OW=',#strprog)+4,CASE WHEN CHARINDEX('&',#strprog,CHARINDEX('&OW=',#strprog)+4 ) <=0 THEN LEN(#strprog)+1 ELSE CHARINDEX('&',#strprog,CHARINDEX('&OW=',#strprog)+4 ) END -CHARINDEX('&OW=',#strprog)-4 )
SELECT #OW
SET #OV= SUBSTRING(#strprog,CHARINDEX('&OV=',#strprog)+4,CASE WHEN CHARINDEX('&',#strprog,CHARINDEX('&OV=',#strprog)+4 ) <=0 THEN LEN(#strprog)+1 ELSE CHARINDEX('&',#strprog,CHARINDEX('&OV=',#strprog)+4 ) END -CHARINDEX('&OV=',#strprog)-4 )
SELECT #OV
SET #CV= SUBSTRING(#strprog,CHARINDEX('&CV=',#strprog)+4,CASE WHEN CHARINDEX('&',#strprog,CHARINDEX('&CV=',#strprog)+4 ) <=0 THEN LEN(#strprog)+1 ELSE CHARINDEX('&',#strprog,CHARINDEX('&CV=',#strprog)+4 ) END -CHARINDEX('&CV=',#strprog)-4 )
SELECT #CV
The problem is the link could be cut off at any place
example:-
&OR=-12&MR=24560&MM=20
how can I stop cutting when it reach an end ?
any help ?

You can use XML type in MSSQL server to separate the string into lines and get a table with attribute and value columns. Then you can select any attribute you need from this rowset:
DECLARE #strprog AS NVARCHAR(MAX);
SET #strprog='WorkFlow/WFmain.aspx?sservice=WFAppraisalManage&showrep=1&
applyRole=0&DisableApproval=1&grbCompQuest=1&EnablecompletionChk=1
&MaxCompletionNo=110&ProficiencyValidation=0&objresulttxt=0
&OR=-12&MR=24560&MM=20&Mx=110&EOW=1&OW=50&OV=12&CV=-1';
DECLARE #xml as XML;
SET #xml = cast(('<X>'+replace(#strprog,'&' ,'</X><X>')+'</X>') as xml);
WITH T2 as
(
SELECT N.value('.', 'varchar(1000)') as val FROM #xml.nodes('X') as T(N)
)
SELECT LEFT(val,CHARINDEX('=',val)-1) as attribute,
SUBSTRING(val,CHARINDEX('=',val)+1,1000) as value
FROM T2;
You get following row set:
╔═══════════════════════════════╦═══════════════════╗
║ attribute ║ value ║
╠═══════════════════════════════╬═══════════════════╣
║ WorkFlow/WFmain.aspx?sservice ║ WFAppraisalManage ║
║ showrep ║ 1 ║
║ applyRole ║ 0 ║
║ DisableApproval ║ 1 ║
║ grbCompQuest ║ 1 ║
║ EnablecompletionChk ║ 1 ║
║ MaxCompletionNo ║ 110 ║
║ ProficiencyValidation ║ 0 ║
║ objresulttxt ║ 0 ║
║ OR ║ -12 ║
║ MR ║ 24560 ║
║ MM ║ 20 ║
║ Mx ║ 110 ║
║ EOW ║ 1 ║
║ OW ║ 50 ║
║ OV ║ 12 ║
║ CV ║ -1 ║
╚═══════════════════════════════╩═══════════════════╝

All your substrings are preempted by an equal sign and finished by an ampersand.
Why don't you create a loop to get the string, get value from first equal to the ampersand, drop the part of the string processed from the string and continue doing the same with the rest of the string??

You can check the CHARINDEX of each token before setting each variable to see if that token even exists at all in the string.

DECLARE #strprog AS NVARCHAR(MAX)--,#strLM AS NVARCHAR(20),#strLOW AS NVARCHAR(20),#strLOR AS NVARCHAR(20),#strLCR AS NVARCHAR(20),
DECLARE #OR AS SMALLINT, #MR AS SMALLINT, #MM AS SMALLINT, #Mx AS SMALLINT, #EOW AS SMALLINT, #OW AS SMALLINT, #OV AS SMALLINT, #CV AS SMALLINT
--SELECT #MR =0, #MM =0, #Mx=0 , #EOW=0, #OW =0, #OV=0
--&OR=-12&MR=24560&MM=20&Mx=110&EOW=1&OW=50&OV=12&CV=-1
SET #strprog='WorkFlow/WFmain.aspx?sservice=WFAppraisalManage&showrep=1&applyRole=0&DisableApproval=1&grbCompQuest=1&EnablecompletionChk=1
&MaxCompletionNo=110&ProficiencyValidation=0&objresulttxt=0
&OR=-12&MR=24560&Mx=110&EOW=1&OW=50&OV=12'
IF CHARINDEX('&OR=',#strprog) <> 0
SET #OR= SUBSTRING(#strprog,CHARINDEX('&OR=',#strprog)+4,CASE WHEN CHARINDEX('&',#strprog,CHARINDEX('&OR=',#strprog)+4 ) <=0 THEN LEN(#strprog)+1 ELSE CHARINDEX('&',#strprog,CHARINDEX('&OR=',#strprog)+4 ) END -CHARINDEX('&OR=',#strprog)-4 )
SELECT #OR
IF CHARINDEX('&MR=',#strprog) <> 0
SET #MR= SUBSTRING(#strprog,CHARINDEX('&MR=',#strprog)+4,CASE WHEN CHARINDEX('&',#strprog,CHARINDEX('&MR=',#strprog)+4 ) <=0 THEN LEN(#strprog)+1 ELSE CHARINDEX('&',#strprog,CHARINDEX('&MR=',#strprog)+4 ) END -CHARINDEX('&MR=',#strprog)-4 )
SELECT #MR
IF CHARINDEX('&MM=',#strprog) <> 0
SET #MM= SUBSTRING(#strprog,CHARINDEX('&MM=',#strprog)+4,CASE WHEN CHARINDEX('&',#strprog,CHARINDEX('&MM=',#strprog)+4 ) <=0 THEN LEN(#strprog)+1 ELSE CHARINDEX('&',#strprog,CHARINDEX('&MM=',#strprog)+4 ) END -CHARINDEX('&MM=',#strprog)-4 )
SELECT #MM
IF CHARINDEX('&Mx=',#strprog) <> 0
SET #Mx= SUBSTRING(#strprog, CHARINDEX('&Mx=',#strprog)+4 ,CASE WHEN CHARINDEX('&',#strprog,CHARINDEX('&Mx=',#strprog)+4 ) <=0 THEN LEN(#strprog)+1 ELSE CHARINDEX('&',#strprog,CHARINDEX('&Mx=',#strprog)+4 ) END -CHARINDEX('&Mx=',#strprog)-4 )
SELECT #Mx
IF CHARINDEX('&EOW=',#strprog) <> 0
SET #EOW= SUBSTRING(#strprog,CHARINDEX('&EOW=',#strprog)+5,CASE WHEN CHARINDEX('&',#strprog,CHARINDEX('&EOW=',#strprog)+5 ) <=0 THEN LEN(#strprog)+1 ELSE CHARINDEX('&',#strprog,CHARINDEX('&EOW=',#strprog)+5 ) END -CHARINDEX('&EOW=',#strprog)-5 )
SELECT #EOW
IF CHARINDEX('&OW=',#strprog) <> 0
SET #OW= SUBSTRING(#strprog,CHARINDEX('&OW=',#strprog)+4,CASE WHEN CHARINDEX('&',#strprog,CHARINDEX('&OW=',#strprog)+4 ) <=0 THEN LEN(#strprog)+1 ELSE CHARINDEX('&',#strprog,CHARINDEX('&OW=',#strprog)+4 ) END -CHARINDEX('&OW=',#strprog)-4 )
SELECT #OW
IF CHARINDEX('&OV=',#strprog) <> 0
SET #OV= SUBSTRING(#strprog,CHARINDEX('&OV=',#strprog)+4,CASE WHEN CHARINDEX('&',#strprog,CHARINDEX('&OV=',#strprog)+4 ) <=0 THEN LEN(#strprog)+1 ELSE CHARINDEX('&',#strprog,CHARINDEX('&OV=',#strprog)+4 ) END -CHARINDEX('&OV=',#strprog)-4 )
SELECT #OV
IF CHARINDEX('&CV=',#strprog) <> 0
Set #CV= SUBSTRING(#strprog,CHARINDEX('&CV=',#strprog)+4,CASE WHEN CHARINDEX('&',#strprog,CHARINDEX('&CV=',#strprog)+4 ) <=0 THEN LEN(#strprog)+1 ELSE CHARINDEX('&',#strprog,CHARINDEX('&CV=',#strprog)+4 ) END -CHARINDEX('&CV=',#strprog)-4 )
SELECT #CV

Related

SQL: Convert Data For One Column To Multiple Columns

I have tried looking for a solution to my problem using things such as Pivot, but they do not seem to give the output for which I am looking as they appear to map specific values in a row to a column.
I have two columns, the first which contains a "key" field and the second which is changing data. e.g.
╔══════════╦══════════════════╗
║ Project ║ Location ║
╠══════════╬══════════════════╣
║ ProjectA ║ \\Server1\Share1 ║
║ ProjectA ║ \\Server2\Share1 ║
║ ProjectB ║ \\Server6\Share2 ║
║ ProjectB ║ \\Server1\Share2 ║
║ ProjectB ║ \\Server2\Share3 ║
║ ProjectC ║ \\Server8\Share2 ║
║ ProjectD ║ \\Server5\Share9 ║
║ ProjectD ║ \\ServerX\ShareY ║
╚══════════╩══════════════════╝
The output that I am trying to achieve is as follows:
╔══════════╦══════════════════╦══════════════════╦══════════════════╦═════════╦══════════╦═════════╗
║ Project ║ Column1 ║ Column2 ║ Column3 ║ Column4 ║ Column5 ║ ColumnX ║
╠══════════╬══════════════════╬══════════════════╬══════════════════╬═════════╬══════════╬═════════╣
║ ProjectA ║ \\Server1\Share1 ║ \\Server2\Share1 ║ NULL ║ NULL ║ NULL ║ ║
║ ProjectB ║ \\Server1\Share2 ║ \\Server2\Share3 ║ \\Server6\Share2 ║ NULL ║ NULL ║ ║
║ ProjectC ║ \\Server8\Share2 ║ NULL ║ NULL ║ NULL ║ NULL ║ ║
║ ProjectD ║ \\Server5\Share9 ║ \\ServerX\ShareY ║ NULL ║ NULL ║ NULL ║ ║
╚══════════╩══════════════════╩══════════════════╩══════════════════╩═════════╩══════════╩═════════╝
If there is no data for the column then it will be NULL.
The number of distinct values in the location column is dynamic and the desired output is a generic column name, with the distinct location value beside the corresponding Project value.
Hopefully someone can help me with this problem as it is driving me mad!
Thanks in advance.
Ninja
Warning:
This solution assume that it will be max 6 column, you can add more for example up to 20.
LiveDemo
Data:
CREATE TABLE #mytable(
Project VARCHAR(80) NOT NULL
,Location VARCHAR(160) NOT NULL
);
INSERT INTO #mytable(Project,Location) VALUES ('ProjectA','\\Server1\Share1');
INSERT INTO #mytable(Project,Location) VALUES ('ProjectA','\\Server2\Share1');
INSERT INTO #mytable(Project,Location) VALUES ('ProjectB','\\Server6\Share2');
INSERT INTO #mytable(Project,Location) VALUES ('ProjectB','\\Server1\Share2');
INSERT INTO #mytable(Project,Location) VALUES ('ProjectB','\\Server2\Share3');
INSERT INTO #mytable(Project,Location) VALUES ('ProjectC','\\Server8\Share2');
INSERT INTO #mytable(Project,Location) VALUES ('ProjectD','\\Server5\Share9');
INSERT INTO #mytable(Project,Location) VALUES ('ProjectD','\\ServerX\ShareY');
INSERT INTO #mytable(Project,Location) VALUES ('ProjectD','\\ServerX\ShareY');
Query:
WITH cte AS
(
SELECT Project, Location,
[rn] = RANK() OVER (PARTITION BY Project ORDER BY Location)
FROM #mytable
)
SELECT
Project
,Column1 = MAX(CASE WHEN rn = 1 THEN Location ELSE NULL END)
,Column2 = MAX(CASE WHEN rn = 2 THEN Location ELSE NULL END)
,Column3 = MAX(CASE WHEN rn = 3 THEN Location ELSE NULL END)
,Column4 = MAX(CASE WHEN rn = 4 THEN Location ELSE NULL END)
,Column5 = MAX(CASE WHEN rn = 5 THEN Location ELSE NULL END)
,Column6 = MAX(CASE WHEN rn = 6 THEN Location ELSE NULL END)
-- ....
-- ,ColumnX = MAX(CASE WHEN rn = X THEN Location ELSE NULL END)
FROM cte
GROUP BY Project;
EDIT:
Truly generic solution that uses Dynamic-SQL and generates Pivoted column list:
LiveDemo2
DECLARE #cols NVARCHAR(MAX),
#cols_piv NVARCHAR(MAX),
#query NVARCHAR(MAX)
,#max INT = 0;
SELECT #max = MAX(c)
FROM (
SELECT Project, COUNT(DISTINCT Location) AS c
FROM #mytable
GROUP BY Project) AS s;
SET #cols = STUFF(
(SELECT ',' + CONCAT('[',c.n, '] AS Column',c.n, ' ')
FROM ( SELECT TOP (1000) n = ROW_NUMBER() OVER (ORDER BY [object_id]) FROM sys.all_objects ORDER BY n)AS c(n)
WHERE c.n <= #max
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'');
SET #cols_piv = STUFF(
(SELECT ',' + CONCAT('[',c.n, '] ')
FROM ( SELECT TOP (1000) n = ROW_NUMBER() OVER (ORDER BY [object_id]) FROM sys.all_objects ORDER BY n)AS c(n)
WHERE c.n <= #max
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'');
-- SELECT #cols;
set #query = N'SELECT Project, ' + #cols + ' from
(
select Project, Location,
[rn] = RANK() OVER (PARTITION BY Project ORDER BY Location)
from #mytable
) x
pivot
(
max(Location)
for rn in (' + #cols_piv + ')
) p ';
-- SELECT #query;
EXEC [dbo].[sp_executesql]
#query;
Credit goes to #lad2025.
I am using Server2005 so these are the modifications that I had to make to get this to work for me:
Couldn't declare and assign a variable in the same line. #max INT = 0;
CONCAT function doesn't exist so replaced with + between the parameters. ', ' + '[' + convert(varchar, c.n) + '] AS Column' + c.n
c.n is of type bigint so I had to CONVERT it to VARCHAR - CONVERT(VARCHAR, c.n)
When generating the table on which to pivot, a GROUP BY needs to be provided otherwise the data appears in a strange column number.
Without GROUP BY
Without the GROUP BY as more data is in the initial #mytable the results display in columns that are not sequential.
╔══════════╦══════════════════╦══════════════════╦══════════════════╦══════╦══════════════════╦══════╗
║ Project ║ Col1 ║ Col2 ║ Col3 ║ Col4 ║ Col5 ║ Col6 ║
╠══════════╬══════════════════╬══════════════════╬══════════════════╬══════╬══════════════════╬══════╣
║ ProjectA ║ \\Server1\Share1 ║ \\Server2\Share1 ║ NULL ║ NULL ║ NULL ║ NULL ║
║ ProjectB ║ \\Server1\Share2 ║ \\Server2\Share3 ║ \\Server6\Share2 ║ NULL ║ NULL ║ NULL ║
║ ProjectC ║ \\Server8\Share2 ║ NULL ║ NULL ║ NULL ║ NULL ║ NULL ║
║ ProjectD ║ NULL ║ \\Server5\Share9 ║ NULL ║ NULL ║ \\ServerX\ShareY ║ NULL ║
╚══════════╩══════════════════╩══════════════════╩══════════════════╩══════╩══════════════════╩══════╝
With GROUP BY As it should be.
╔══════════╦══════════════════╦══════════════════╦══════════════════╦══════╦══════╦══════╗
║ Project ║ Col1 ║ Col2 ║ Col3 ║ Col4 ║ Col5 ║ Col6 ║
╠══════════╬══════════════════╬══════════════════╬══════════════════╬══════╬══════╬══════╣
║ ProjectA ║ \\Server1\Share1 ║ \\Server2\Share1 ║ NULL ║ NULL ║ NULL ║ NULL ║
║ ProjectB ║ \\Server1\Share2 ║ \\Server2\Share3 ║ \\Server6\Share2 ║ NULL ║ NULL ║ NULL ║
║ ProjectC ║ \\Server8\Share2 ║ NULL ║ NULL ║ NULL ║ NULL ║ NULL ║
║ ProjectD ║ \\Server5\Share9 ║ \\ServerX\ShareY ║ NULL ║ NULL ║ NULL ║ NULL ║
╚══════════╩══════════════════╩══════════════════╩══════════════════╩══════╩══════╩══════╝
Data:
CREATE TABLE #mytable(
Project VARCHAR(80) NOT NULL
,Location VARCHAR(160) NOT NULL
);
INSERT INTO #mytable(Project,Location) VALUES ('ProjectA','\\Server1\Share1');
INSERT INTO #mytable(Project,Location) VALUES ('ProjectA','\\Server2\Share1');
INSERT INTO #mytable(Project,Location) VALUES ('ProjectB','\\Server6\Share2');
INSERT INTO #mytable(Project,Location) VALUES ('ProjectB','\\Server1\Share2');
INSERT INTO #mytable(Project,Location) VALUES ('ProjectB','\\Server2\Share3');
INSERT INTO #mytable(Project,Location) VALUES ('ProjectC','\\Server8\Share2');
INSERT INTO #mytable(Project,Location) VALUES ('ProjectD','\\Server5\Share9');
INSERT INTO #mytable(Project,Location) VALUES ('ProjectD','\\ServerX\ShareY');
Query:
DECLARE #cols NVARCHAR(MAX),
#cols_piv NVARCHAR(MAX),
#query NVARCHAR(MAX)
,#max INT;
SELECT #max = MAX(c)
FROM (
SELECT Project, COUNT(DISTINCT Location) AS c
FROM #mytable
GROUP BY Project) AS s;
SET #cols = STUFF(
(SELECT ', ' + '[' + convert(varchar, c.n) + '] AS Column' + convert(varchar, c.n)
FROM ( SELECT TOP (1000) n = ROW_NUMBER() OVER (ORDER BY [object_id]) FROM sys.all_objects ORDER BY n)AS c(n)
WHERE c.n <= #max
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)')
,1,1,'');
SET #cols_piv = STUFF(
(SELECT ',' + '[' + convert(varchar, c.n) + ']'
FROM ( SELECT TOP (1000) n = ROW_NUMBER() OVER (ORDER BY [object_id]) FROM sys.all_objects ORDER BY n)AS c(n)
WHERE c.n <= #max
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)')
,1,1,'');
set #query = N'SELECT Project, ' + #cols + ' from
(
select Project, Location,
[rn] = RANK() OVER (PARTITION BY Project ORDER BY Location)
from #mytable
GROUP BY Project, Location
) x
pivot
(
max(Location)
for rn in (' + #cols_piv + ')
) p ';
EXEC [dbo].[sp_executesql]
#query;

Break up Text string to use in Join in SQL Server 2008

Good Day,
I am busy creating a tax document for discount entries out of our debtors transaction table. One of the requirements is that the discount amount must indicate to which transaction it relates. This information is available but I do not know how to use it because it is in a single delimited text field. The table looks as follows.
╔═════════╦════════════╦══════╦════════╦═══════════╦══════════════════════════════════════════╗
║ AutoIdx ║ TxDate ║ Id ║ Amount ║ Reference ║ cAllocs ║
╠═════════╬════════════╬══════╬════════╬═══════════╬══════════════════════════════════════════╣
║ 1 ║ 30-07-2014 ║ OInv ║ 100 ║ IN543 ║ I=4;A=99;D=20140730|I=3;A=1;D=20140730 ║
║ 2 ║ 30-07-2014 ║ OInv ║ 200 ║ IN544 ║ I=4;A=198;D=20140730|I=3;A=2;D=20140730 ║
║ 3 ║ 30-07-2014 ║ DS ║ 3 ║ DISC ║ I=1;A=1;D=20140730|I=2;A=2;D=20140730 ║
║ 4 ║ 30-07-2014 ║ Pmnt ║ 297 ║ PMNT ║ I=1;A=99;D=20140730|I=2;A=198;D=20140730 ║
╚═════════╩════════════╩══════╩════════╩═══════════╩══════════════════════════════════════════╝
In the column cAllocs you have the information that links the lines with each other. It has three indicators
I - the line in this table that it links to
A - the amount of the link transaction
D - the date of the transaction
It can have only one set of these or many sets delimited with a pipe character.
I want to end up with a table that has only the entries with Id "DS" but giving me the reference column value of the lines it links to in a single column (they can be delimited by comma)
So from the above table I would like to get this result.
╔════════════╦════╦════════╦═══════════╦═════════════╗
║ TxDate ║ Id ║ Amount ║ Reference ║ MatchedTo ║
╠════════════╬════╬════════╬═══════════╬═════════════╣
║ 30-07-2014 ║ DS ║ 3 ║ DISC ║ IN543,IN544 ║
╚════════════╩════╩════════╩═══════════╩═════════════╝
Thanks for any replies
You can create a function that handles string manipulation and retrieves the data at the same time. Try this one below:
CREATE FUNCTION [dbo].[GetReferenceFromCAllocs](#cAllocs VARCHAR(MAX))RETURNS VARCHAR(MAX)AS
BEGIN
DECLARE #val AS VARCHAR(MAX)
DECLARE #ch AS CHAR(1)
DECLARE #result AS VARCHAR(MAX)
DECLARE #returnValue AS VARCHAR(MAX)
SET #val = #cAllocs
SET #result = ''
SET #returnValue = ''
DECLARE #idx INT
SET #idx = 1
WHILE #idx < LEN(#val)
BEGIN
SET #ch = SUBSTRING(#val,#idx,1)
IF(#ch = 'I')
BEGIN
DECLARE #idx2 INT
DECLARE #temp VARCHAR(5)
SET #temp = #ch
SET #idx2 = #idx + 1
SET #ch = SUBSTRING(#val,#idx2,1)
WHILE #ch <> ';'
BEGIN
SET #temp = #temp + #ch
SET #idx2 = #idx2 + 1
SET #ch = SUBSTRING(#val,#idx2,1)
END
SET #idx = #idx2
SET #result = #result + #temp + ';'
END
ELSE
SET #idx = #idx + 1
END
SET #idx = 1
WHILE #idx < LEN(#result)
BEGIN
SET #ch = SUBSTRING(#val,#idx,1)
IF(#ch = '=')
BEGIN
SET #temp = ''
SET #idx2 = #idx + 1
SET #ch = SUBSTRING(#result,#idx2,1)
WHILE #ch <> ';'
BEGIN
SET #temp = #temp + #ch
SET #idx2 = #idx2 + 1
SET #ch = SUBSTRING(#result,#idx2,1)
END
SET #idx = #idx2
DECLARE #tempValue AS VARCHAR(10)
SET #tempValue = ''
SELECT #tempValue = Reference FROM YOUR_TABLE
WHERE AutoIdx = #temp
SET #returnValue = #returnValue + #tempValue + ','
END
ELSE
SET #idx = #idx + 1
END
RETURN SUBSTRING(#returnValue,1, LEN(#returnValue)-1)
END
--EDIT: The function above will get all data with I as it's prefix:
E.g:
Passed Value: I=4;A=99;D=20140730|I=3;A=1;D=20140730
Result Value: I=4;I=3;
After that, it queries the corresponding Reference with AutoIdx 4 and 3; then returns it in a format as specified in your question.
Then, run this sql:
SELECT
yt.TxDate,
yt.Id,
yt.Amount,
yt.Reference,
dbo.GetReferenceFromCAllocs(yt.cAllocs) AS MatchedTo
FROM YOUR_TABLE yt
WHERE yt.Id = 'DS'
Hope this would work. Just adjust the SQL codes to optimize it. I'm in a hurry so I used safest values (such as VARCHAR(MAX)).

update table with values in related records

I have a table which must be with next structure:
╔════╦═══════╦════╦═════╗
║ id ║ a ║ c ║ b ║
╠════╬═══════╬════╬═════╣
║ 55 ║ 56;57 ║ ║ P25 ║
║ 56 ║ ║ 56 ║ 25 ║
║ 57 ║ ║ 57 ║ 25 ║
╚════╩═══════╩════╩═════╝
where:
1) record with id=55 is a parent record and
2) records with id=56, id=57 (listed in a column and separated with semicolon) are child records
At first table is next
╔════╦═══════╦════╦═════╗
║ id ║ a ║ c ║ b ║
╠════╬═══════╬════╬═════╣
║ 55 ║ 56;57 ║ ║ ║
║ 56 ║ ║ 56 ║ ║
║ 57 ║ ║ 57 ║ ║
╚════╩═══════╩════╩═════╝
so I must to update table such as first table
For this purpose I created next CTE
with My_CTE(PId, a, c, b, newC, inde) as
(
select
ST.PID, ST.a, ST.c, ST.b, res.C,
ind = case
when ST.a != ''
then (dense_rank() over(order by ST.a))
end
from STable as ST
outer APPLY
fnSplit(ST.a) as res
where (not(ST.a = '') or not(ST.c = ''))
)
UPDATE STable
Set b =
cte.inde
From STable as st
Join My_CTE as cte on st.PID = cte.PId;
GO
As a result I have table with next values
╔════╦═══════╦════╦═════╗
║ id ║ a ║ c ║ b ║
╠════╬═══════╬════╬═════╣
║ 55 ║ 56;57 ║ ║ 25 ║
║ 56 ║ ║ 56 ║ ║
║ 57 ║ ║ 57 ║ ║
╚════╩═══════╩════╩═════╝
So I need to set values in column b for children records.
Maybe it could be established in select statement of MyCTE?
Help please
I am not entirely sure if I understand your request correctly so apologies if I have misunderstood.
So you have already managed to get the result set to here
╔════╦═══════╦════╦═════╗
║ id ║ a ║ c ║ b ║
╠════╬═══════╬════╬═════╣
║ 55 ║ 56;57 ║ ║ 25 ║
║ 56 ║ ║ 56 ║ ║
║ 57 ║ ║ 57 ║ ║
╚════╩═══════╩════╩═════╝
Now you want this to look like this
╔════╦═══════╦════╦═════╗
║ id ║ a ║ c ║ b ║
╠════╬═══════╬════╬═════╣
║ 55 ║ 56;57 ║ ║ P25 ║
║ 56 ║ ║ 56 ║ 25 ║
║ 57 ║ ║ 57 ║ 25 ║
╚════╩═══════╩════╩═════╝
Please see if this script works for you.
IF OBJECT_ID(N'tempdb..#temp')>0
DROP TABLE #temp
IF OBJECT_ID(N'tempdb..#temp1')>0
DROP TABLE #temp1
CREATE TABLE #temp (id int, a varchar(100),c varchar(100),b varchar(100))
INSERT INTO #temp VALUES ('55','56;57',' ','25')
INSERT INTO #temp VALUES ('56',' ','56',' ')
INSERT INTO #temp VALUES ('57',' ','57',' ')
SELECT * FROM #temp t
SELECT y.id, fn.string AS a,y.b
INTO #temp1
FROM #temp AS y
CROSS APPLY dbo.fnParseStringTSQL(y.a, ';') AS fn
--SELECT * FROM #temp1
UPDATE t
SET t.b=CASE WHEN CHARINDEX(';',t.a)>0 THEN 'P'+t.b ELSE t1.b END
FROM #temp t
LEFT JOIN #temp1 t1
ON t.id = t1.a
--DROP TABLE #temp
SELECT * FROM #temp t
IF OBJECT_ID(N'tempdb..#temp')>0
DROP TABLE #temp
IF OBJECT_ID(N'tempdb..#temp1')>0
DROP TABLE #temp1
Function borrowed from this link
CREATE FUNCTION [dbo].[fnParseStringTSQL]
(
#string NVARCHAR(MAX),
#separator NCHAR(1)
)
RETURNS #parsedString TABLE (string NVARCHAR(MAX))
AS
BEGIN
DECLARE #position INT
SET #position = 1
SET #string = #string + #separator
WHILE CHARINDEX(#separator, #string, #position) <> 0
BEGIN
INSERT INTO #parsedString
SELECT SUBSTRING(
#string,
#position,
CHARINDEX(#separator, #string, #position) - #position
)
SET #position = CHARINDEX(#separator, #string, #position) + 1
END
RETURN
END
This is really not an ideal data structure, but the following will do it...
CREATE TABLE #STable
(
id int primary key clustered
, a varchar(500)
, c varchar(500)
, b varchar(500)
)
INSERT INTO #STable
(id, a, c, b)
VALUES (55, '56;57', '', '25')
, (56, '', '56', '')
, (57, '', '57', '')
/* >>>>> Get all parents <<<<< */
CREATE TABLE #folks
(
sno int identity(1,1)
, id int
, a varchar(500)
)
CREATE TABLE #family
(
parent int
, child int
)
INSERT INTO #folks
(id, a)
SELECT id, a
FROM #STable
WHERE a <> ''
DECLARE #NID int
, #XID int
, #parent int
, #Children varchar(500)
, #Child int
SELECT #NID = MIN(sno), #XID = MAX(sno)
FROM #folks
/* >>>>> Loop to figure out the children <<<<< */
WHILE #NID <= #XID
BEGIN
SELECT #parent = id, #Children = a
FROM #folks
WHERE sno = #NID
WHILE LEN(#Children) > 0
BEGIN
IF CHARINDEX(';', #Children) > 0
BEGIN
SET #Child = CAST(LEFT(#Children, CHARINDEX(';', #Children) -1) as int)
SET #Children = RIGHT(#Children, LEN(#Children) - CHARINDEX(';', #Children))
INSERT INTO #family
(parent, child)
VALUES (#parent, #Child)
END
ELSE
BEGIN
SET #Child = CAST(#Children AS INT)
SET #Children = ''
INSERT INTO #family
(parent, child)
VALUES (#parent, #Child)
END
END
SET #NID = #NID + 1
END
/* >>>>> Update <<<<< */
UPDATE c
SET b = p.b
FROM #family f
INNER JOIN #STable p
on f.parent = p.id
INNER JOIN #STable c
on f.child = c.id
SELECT *
FROM #STable
DROP TABLE #STable
DROP TABLE #folks
DROP TABLE #family

Use result of one query in another sql query

I have below table structure :
Table1
╔═════╦══════╦═════════════╦═════════════╗
║Col1 ║ Col2 ║ TableName ║ ColumnName ║
╠═════╬══════╬═════════════╬═════════════╣
║ 1 ║ abc ║ Table2 ║ column2 ║
║ 2 ║ xyz ║ ║ ║
║ 3 ║ pqr ║ Table1 ║ column1 ║
║ 4 ║ jbn ║ ║ ║
╚═════╩════════════════════╩═════════════╝
Table2 :
╔════════╦═════════╗
║Column1 ║ Column2 ║
╠════════╬═════════╣
║ 1 ║ A ║
║ 2 ║ B ║
║ 3 ║ C ║
║ 4 ║ D ║
╚════════╩═════════╝
Table3
╔════════╦═════════╗
║Column1 ║ Column2 ║
╠════════╬═════════╣
║ 1 ║ X ║
║ 2 ║ Y ║
║ 3 ║ Z ║
║ 4 ║ A ║
╚════════╩═════════╝
I want to write stored procedure which will select data from Table1 and data from another table depending upon value of column tableName and columnName in Table1.
I want data in following format:
╔═════╦═════╦════════╗
║Col1 ║ Col2║ List ║
╠═════╬═════╬════════╣
║ 1 ║ abc ║A,B,C,D ║
║ 2 ║ xyz ║ ║
║ 3 ║ pqr ║1,2,3,4 ║
║ 4 ║ jbn ║ ║
╚═════╩═════╩════════╝
Try temporary table .
look at here : http://www.sqlteam.com/article/temporary-tables
You will need a dynamic sql to get such a select. Check out the link http://www.mssqltips.com/sqlservertip/1160/execute-dynamic-sql-commands-in-sql-server/
EDIT:
The following code should do the trick.
I have assumed that the column Col1 in Table1 is of type int.
I have used Temp table to generate the required table. You can replace it will your table as per your convenience. Also I have used #table1 which you can replace with your Table1.
Also this might not be very good in terms of performance but this is the best I could come up with right now.
declare #count int, #Query VARCHAR(5000), #counter int, #tableName VARCHAR(50), #ColumnName VARCHAR(50), #Col1 INT, #Col2 VARCHAR(50)
select #count = count(0) from #table1
SET #counter = 1
CREATE TABLE #table4
(
Col1 INT,
Col2 VARCHAR(50),
List VARCHAR(50)
)
WHILE #counter <= #count
BEGIN
SELECT #tableName = TableName, #ColumnName = columnName, #Col1 = Col1, #Col2 = Col2 FROM #Table1 WHERE Col1 = #counter
SELECT #Query = 'INSERT INTO #table4 (Col1 , Col2) VALUES (' + CONVERT(varchar(50),#Col1) + ', ''' + #Col2 + ''')'
EXEC (#Query)
SELECT #Query = ''
IF ISNULL(#tableName, '') != '' AND ISNULL(#ColumnName, '') != ''
BEGIN
SELECT #Query = 'UPDATE #table4 SET LIST = STUFF((SELECT '','' + CONVERT(VARCHAR(50), ' + #ColumnName + ') FROM ' + #tableName + ' FOR XML PATH('''')),1,1,'''') WHERE Col1 = ' + CONVERT(varchar(50),#Col1)
EXEC (#Query)
END
SET #counter = #counter + 1
END
SELECT * FROM #table4
Hope this helps
Answer edited for incorporating n no of rows
CODE
drop table #temp1
declare #tabletemp as varchar (10)
declare #columntemp as varchar (10)
Declare #sqlTemp NVARCHAR(400)
declare #counter1 as int
declare #counter2 as int
select #counter2 = 1
select name,cname into #temp1 from test1
select #counter1 = COUNT(*) from #temp1
while (#counter2<= #counter1)
begin
SET #tabletemp = (SELECT MIN(name) FROM #temp1)
select #columntemp = (select min(cname) from #temp1 where #temp1.name = #tabletemp)
set #sqlTemp='select '+#columntemp +' from '+#tabletemp
exec(#sqlTemp)
delete from #temp1 where name = #tabletemp and cname = #columntemp
select #counter1 = COUNT(*) from #temp1
end
RESULT
select * from test1
name cname
table1 column1
test2 colname
table1 test
test2 name
select column1 from table1
column1
qwer
asdff
zxcvb
qwer
asdff
zxcvb
select colname from test2
colname
testing
wer
ewrth
sfsf
testing
wer
ewrth
sfsf
select test from table1
--got error message inavlid column nae 'test' as this column does not exist
select name from test2
name
table1
table1
table3
table2
table1
table1
table3
table2

SQL Server query for grouping [duplicate]

This question already has answers here:
SQL Server: Examples of PIVOTing String data
(7 answers)
Closed 8 years ago.
I have a table with 2 columns plateno and alerts.
table Data
plateno alerts
A 1
B 2
C 2
A 3
B 2
A 4
A 1
Now I want to get the result like this:
alerts-> 1 2 3 4
---------------------------
A 2 0 1 1
B 0 2 0 0
C 0 1 0 0
I mean 'A' has two alerts of type '1',1 alert of type '3' and '4'...and so on..
Here is my query I am trying with
Select count(alert)
from mytable
group by plateno
Try this :-
Select plateno,[1],[2],[3],[4]
from
(
Select plateno,alerts from Sample
)p
pivot
(
count(alerts)
for alerts in ([1],[2],[3],[4])
)pvt
Demo Here
If you have unknown number of values for the column Alert, a dynamic sql is much more preferred,
DECLARE #colList AS NVARCHAR(MAX), #sqlStatement AS NVARCHAR(MAX)
SELECT #colList = STUFF((SELECT DISTINCT ',' + QUOTENAME(alerts)
FROM data
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)') ,1,1,'')
SET #sqlStatement = 'SELECT plateno,' + #colList + '
FROM
(
SELECT plateno, alerts
FROM data
) dta
pivot
(
COUNT(alerts)
FOR alerts IN (' + #colList + ')
) pvt '
EXECUTE(#sqlStatement)
SQLFiddle Demo
OUTPUT
╔═════════╦═══╦═══╦═══╦═══╗
║ PLATENO ║ 1 ║ 2 ║ 3 ║ 4 ║
╠═════════╬═══╬═══╬═══╬═══╣
║ A ║ 2 ║ 0 ║ 1 ║ 1 ║
║ B ║ 0 ║ 2 ║ 0 ║ 0 ║
║ C ║ 0 ║ 1 ║ 0 ║ 0 ║
╚═════════╩═══╩═══╩═══╩═══╝
Try this:
SELECT plateno
,SUM(CASE alerts WHEN 1 THEN 1 ELSE 0 END) AS [1]
,SUM(CASE alerts WHEN 2 THEN 1 ELSE 0 END) AS [2]
,SUM(CASE alerts WHEN 3 THEN 1 ELSE 0 END) AS [3]
,SUM(CASE alerts WHEN 4 THEN 1 ELSE 0 END) AS [4]
FROM Table1
GROUP BY plateno
Output:
╔═════════╦═══╦═══╦═══╦═══╗
║ PLATENO ║ 1 ║ 2 ║ 3 ║ 4 ║
╠═════════╬═══╬═══╬═══╬═══╣
║ A ║ 2 ║ 0 ║ 1 ║ 1 ║
║ B ║ 0 ║ 2 ║ 0 ║ 0 ║
║ C ║ 0 ║ 1 ║ 0 ║ 0 ║
╚═════════╩═══╩═══╩═══╩═══╝
See this SQLFiddle
Select plateno,sum([1]) as [1],sum([2]) as [2],sum([3]) as [3],sum([4]) as [4]
from
(
Select plateno,
case when alert=1 then 1 else 0 end as [1],
case when alert=2 then 1 else 0 end as [2],
case when alert=3 then 1 else 0 end as [3],
case when alert=4 then 1 else 0 end as [4]
from planet
)z
group by plateno
SQL fiddle here