SQL separate for stored procedure - sql

About SQL Server Management Studio stored procedure.
The following variables '|' I want to separate it from. How can I do it?
'628391|28100|8304|3|1201129|12|Kg|M01|SERIOUSLY CHUNKY WOOL'
'627452|70462|618|60|100059|72|Ad|M01|THICK & QUICK STRIPES'
'617213|99233|89|10|18754|12|Kg|M01|FASHION KC ARAN 400'

You can use the following query
DECLARE #PL AS VARCHAR(MAX)='628391|28100|8304|3|1201129|12|Kg|M01|SERIOUSLY CHUNKY WOOL'
SELECT value FROM string_split(#PL,'|')
+-----------------------+
| value |
+-----------------------+
| 628391 |
| 28100 |
| 8304 |
| 3 |
| 1201129 |
| 12 |
| Kg |
| M01 |
| SERIOUSLY CHUNKY WOOL |
+-----------------------+
EDIT
DROP TABLE IF EXISTS TestStrList
DECLARE #query AS NVARCHAR(MAX)
DECLARE #PL AS VARCHAR(MAX)='628391|28100|8304|3|1201129|12|Kg|M01|SERIOUSLY CHUNKY WOOL'
DECLARE #cOL AS VARCHAR(MAX)
SELECT value INTO
TestStrList FROM string_split(#PL,'|')
select #cOL = STUFF((SELECT ',' + QUOTENAME(value)
FROM TestStrList
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = N'SELECT ' + #cOL + N' from
(
select value
from TestStrList
) x
pivot
(
max(value)
for value in (' + #cOL + N')
) p '
exec sp_executesql #query;
+--------+-------+------+---+---------+----+----+-----+-----------------------+
| 628391 | 28100 | 8304 | 3 | 1201129 | 12 | Kg | M01 | SERIOUSLY CHUNKY WOOL |
+--------+-------+------+---+---------+----+----+-----+-----------------------+
| 628391 | 28100 | 8304 | 3 | 1201129 | 12 | Kg | M01 | SERIOUSLY CHUNKY WOOL |
+--------+-------+------+---+---------+----+----+-----+-----------------------+

I'm not sure what you mean with "seperate it" but the following SQL QUERY might be helpful for you.
DECLARE #text VARCHAR(200) = '617213|99233|89|10|18754|12|Kg|M01|FASHION KC ARAN 400'
WHILE 1 = 1
BEGIN
DECLARE #index INT = CHARINDEX('|', #text, 1)
IF #index = 0
RETURN
SELECT LEFT(#text, #index - 1)
SET #text = SUBSTRING(#text, #index + 1, LEN(#text) - #index)
END
Basically, the code with using CHARINDEX, is trying to find out the index of the first pipe (from left) in the text. After finding the index, it's applies a SUBSTRING to get exact text.

Related

Dynamic Statement to Get Top "n" for Each Individual Column from Ms SQL Table

Help me with this Dynamic statement perform faster, the statement will fetch top n values for each column from a table.
The table will have an "n" number of columns but will have a primary key. NULLs couldn't have been avoided as any other value is considered as VALID and should go to the database.
Table
+-------+------+------+------+
| Depth | RPMA | ROP | WOB |
+-------+------+------+------+
| 6111 | 72 | 14.6 | 0 |
| 6110 | 72 | 14.1 | 1 |
| 6109 | 66 | 15.2 | NULL |
| 6108 | 68 | 14 | NULL |
| 6107 | 69 | 14 | NULL |
| 6106 | 61 | 14.8 | NULL |
| 6105 | 70 | NULL | NULL |
| 6104 | 64 | NULL | NULL |
| 6103 | 59 | NULL | NULL |
| 6102 | 49 | NULL | NULL |
+-------+------+------+------+
Result set,
+-------+------+------+------+
| Depth | RPMA | ROP | WOB |
+-------+------+------+------+
| 6111 | 72 | NULL | 0 |
| 6110 | 72 | NULL | 1 |
| 6109 | NULL | 15.2 | NULL |
| 6106 | NULL | 14.8 | NULL |
+-------+------+------+------+
Dynamic SQL used to get current result set,
DECLARE #Columns VARCHAR(MAX); -- Param1
DECLARE #IdxColumn VARCHAR(250); --Param2
DECLARE #Limit VARCHAR(11); --Param3
DECLARE #SQL NVARCHAR(MAX)=''; --Param4
DECLARE #query NVARCHAR(MAX) = ' SELECT TOP (' + #pLimit + ') ' + #IdxColumn + ', ' + #Columns + ' FROM [Table] WHERE '
SET #SQL = #query + REPLACE(#Columns,',', ' IS NOT NULL ORDER BY '+ #IdxColumn + ' ASC ' + N' UNION' + #query) + ' IS NOT NULL ORDER BY ' + #IdxColumn
SET #SQL = 'SELECT * FROM ('+#SQL+') T ORDER BY ' + #IdxColumn + ' ASC'
EXEC (#SQL)
The following query should work for the sample data:
WITH cte AS (
SELECT *
, DENSE_RANK() OVER (ORDER BY RPMA DESC) AS RPMA_RANK
, DENSE_RANK() OVER (ORDER BY ROP DESC) AS ROP_RANK
, DENSE_RANK() OVER (ORDER BY WOB DESC) AS WOB_RANK
FROM t
)
SELECT Depth
, CASE WHEN RPMA_RANK <= 2 THEN RPMA END
, CASE WHEN ROP_RANK <= 2 THEN ROP END
, CASE WHEN WOB_RANK <= 2 THEN WOB END
FROM cte
WHERE RPMA_RANK <= 2
OR ROP_RANK <= 2
OR WOB_RANK <= 2
Note that it returns three rows for RPMA column (there are two 72 and one 70). For n number of columns you need to use dynamic SQL.
This doesn't answer the question, but does fix the terrifying security flaws in the above.
There are multiple problems with the above, so please note that this is a significant but needed change to the SQL you have. Right now you are injecting unsantised parameters into your code, and also using datatypes that are vastly too large. #Columns is varchar(MAX), meaning that someone has 2GB worth of characters to inject into your system. #IdxColumn is a varchar(250) and references a single column; an object can at most be 128 characters long so there's no need for the other 122 characters. Also #Limit is a varchar, despite being an int and should be a parameter.
Firstly, rather than using a varchar(MAX) for #Columns I suggest a table type object:
CREATE TYPE dbo.ObjectList (ObjectName sysname);
sysname is a synonym of nvarchar(128) NOT NULL; and is the data type used for object names in SQL Server. You'll then need to INSERT the names of the columns into a declared table type parameter; one row for each Column Name
Then we can safely inject and parametrise your query:
--Parameters
DECLARE #Columns dbo.ObjectList,
#IdxColumn sysname, --sysname as well
#Limit int; --not varchar
--Variables needed in the SQL:
DECLARE #SQL nvarchar(MAX),
#CRLF nchar(2) = NCHAR(13) + NCHAR(10);
SET #SQL = N'SELECT TOP (#Limit)' + #CRLF +
N' ' + QUOTENAME(#IdxColumn) + N',' + #CRLF +
STUFF((SELECT N',' + #CRLF +
N' ' + QUOTENAME(C.ObjectName)
FROM #Columns C
FOR XML PATH(N''),TYPE).value('.','nvarchar(MAX)'),1,3,N'') + #CRLF +
N'FROM dbo.[Table]' + #CRLF + --Should dbo.[Table] also not be safely injected?
N'WHERE ' +
STUFF((SELECT #CRLF +
N' OR ' + QUOTENAME(C.ObjectName) + N' IS NOT NULL'
FROM #Columns C
FOR XML PATH(N''),TYPE).value('.','nvarchar(MAX)'),1,8,N'') + #CRLF +
N'ORDER BY ' + QUOTENAME(#IdxColumn) + N' ASC;'
EXEC sp_executesql #SQL, N'#Limit int', #Limit;

Dynamic Pivot Table across multiple columns

I'm a fairly new to SQL and could use some help. I have a table of time sheet data with a separate time sheet on each row. Each time sheet has a column with jobcode1 to jobcode16 that stores a string indicating a job code. Each of those has a corresponding TotalJob1 to TotalJob16.
I've managed to create a pivot on the JobCode1 no problem with a column for each Job and the total from TotalJob1. I used this to build it.
DECLARE #DynamicPivotQuery AS NVARCHAR(MAX)
DECLARE #ColumnName AS NVARCHAR(MAX)
SELECT #ColumnName = ISNULL(#ColumnName + ',','') + QUOTENAME(TS_Job1Code)
FROM (SELECT DISTINCT TS_Job1Code FROM
dbo.timesheetData) as timesheetdata
SET #DynamicPivotQuery =
N'SELECT VolumeID, ' + #ColumnName + '
FROM dbo.timesheetData
PIVOT(SUM(TS_TotalJob1)
FOR TS_Job1Code IN (' + #ColumnName + ')) AS PVTTable'
EXEC sp_executesql #DynamicPivotQuery
I'm struggling to iterate over the other Job columns and merge them into one big pivot table and was hoping someone might be able to give me a pointer?
My thought was to try and repeat the step 16 times but I don't think this is even close to the right way.
DECLARE #DynamicPivotQuery AS NVARCHAR(MAX)
DECLARE #ColumnName AS NVARCHAR(MAX)
DECLARE #N AS INT
DECLARE #NCHAR AS NVARCHAR(MAX)
SET #N = 1
WHILE #N < 17
BEGIN
SET #NCHAR = CAST(#N as VARCHAR)
SELECT #ColumnName = ISNULL(#ColumnName + ',','') + QUOTENAME(('TS_Job' +
#NCHAR + 'Code'))
FROM (SELECT DISTINCT ('TS_Job' + #NCHAR + 'Code') FROM
dbo.timesheetData) as timesheetdata
SET #DynamicPivotQuery =
N'SELECT ' + #ColumnName + '
FROM dbo.timesheetData
PIVOT(SUM(TS_TotalJob' + #NCHAR + ')
FOR TS_Job' + #NCHAR + 'Code IN (' + #ColumnName + ')) AS PVTTable'
EXEC sp_executesql #DynamicPivotQuery
SET #N = #N + 1
END
EXEC sp_executesql #SQL
Original
+-------------+----------+----------+----------+-----------+-----------+-----------+
| TimesheetID | JobCode1 | JobCode2 | JobCode3 | TotalJob1 | TotalJob2 | TotalJob3 |
+-------------+----------+----------+----------+-----------+-----------+-----------+
| 1 | J1 | J3 | | 10 | 9 | |
+-------------+----------+----------+----------+-----------+-----------+-----------+
| 2 | J2 | J1 | J3 | 5 | 5 | 5 |
+-------------+----------+----------+----------+-----------+-----------+-----------+
| 3 | J2 | | | 6 | 3 | 1 |
+-------------+----------+----------+----------+-----------+-----------+-----------+
What I want to achieve
+-------------+----+----+----+----+----+
| TimesheetID | J1 | J2 | J3 | J4 | J6 |
+-------------+----+----+----+----+----+
| 1 | 10 | | 9 | | |
+-------------+----+----+----+----+----+
| 2 | 5 | 5 | 5 | | |
+-------------+----+----+----+----+----+
| 3 | | 6 | | 3 | 1 |
+-------------+----+----+----+----+----+
It's going to get painfully complicated, but one thing you can do is to UNPIVOT your data so that it looks like this:
TimesheetId JobCode JobTotal
1 J1 10
1 J3 9
2 J1 5
....
And then PIVOT that derived table to get your desired result.
Like Tab mentioned, you can unpivot your data first and then Pivot it again.
You can use CROSS APPLY and VALUES to unpivot your table into a temp table.
SELECT ca.*
INTO #temp
FROM timesheet
CROSS APPLY (VALUES
(TimesheetID, JobCode1, TotalJob1),
(TimesheetID, JobCode2, TotalJob2),
(TimesheetID, JobCode3, TotalJob3)
) ca(TimesheetID, JobCode, TotalJob)
this gives you a table like
TimesheetID JobCode TotalJob
----------- ------- -----------
1 J1 10
1 J3 9
1 NULL NULL
2 J2 5
2 J1 5
2 J3 5
3 J2 6
3 NULL 3
3 NULL 1
not sure if you'd have null jobcodes in actual data.. but you can eliminate them using Where ca.JobCode IS NOT NULL above
Then you create your dynamic column string from the temp table
DECLARE #JobCodes nvarchar(MAX)
SELECT #JobCodes = COALESCE(#JobCodes + ',','') + QUOTENAME(JobCode)
FROM #temp
GROUP BY JobCode
ORDER BY JobCode
Then build your dynamic pivot. Instead of creating global temp table, just use the same query before in your pivot query.
DECLARE #Sql nvarchar(max)
SET #Sql = N'
SELECT TimeSheetID,' + #JobCodes
+ 'FROM (
SELECT ca.*
FROM timesheet
CROSS APPLY (VALUES
(TimesheetID, JobCode1, TotalJob1),
(TimesheetID, JobCode2, TotalJob2),
(TimesheetID, JobCode3, TotalJob3)
) ca(TimesheetID, JobCode, TotalJob)
) t
PIVOT (
SUM(TotalJob)
FOR JobCode IN (' + #JobCodes + ')
) p'
EXEC sp_executesql #Sql

Split sql string value based on 7th delimiter

Present column value
(Below is column value from temp table, value here is dynamically changing)
45 | 00055 | 9/30/2016 | Vodafone | Randy Singh | Newyork | Test Msg | TBL101 | PC | 1.00 | COMP101 | CS | 1.00.............. etc
Need to divide based on 7th PIPE i.e after Test Msg
Output should be
String1
45 | 00055 | 9/30/2016 | Vodafone | Randy Singh | Newyork | Test Msg
and (as a second string)
String 2
TBL101 | PC | 1.00 | COMP101 | CS | 1.00......... etc
Function
CREATE FUNCTION dbo.SUBSTRING_INDEX
(
#str NVARCHAR(4000),
#delim NVARCHAR(1),
#count INT
)
RETURNS NVARCHAR(4000)
WITH SCHEMABINDING
BEGIN
DECLARE #XmlSourceString XML;
SET #XmlSourceString = (SELECT N'<root><row>' + REPLACE( (SELECT #str AS '*' FOR XML PATH('')) , #delim, N'</row><row>' ) + N'</row></root>');
RETURN STUFF
(
((
SELECT #delim + x.XmlCol.value(N'(text())[1]', N'NVARCHAR(4000)') AS '*'
FROM #XmlSourceString.nodes(N'(root/row)[position() <= sql:variable("#count")]') x(XmlCol)
FOR XML PATH(N''), TYPE
).value(N'.', N'NVARCHAR(4000)')),
1, 1, N''
);
END
GO
DECLARE #EmpId NVARCHAR(1000)
select #EmpId = temp from OMSOrderTemp
SELECT dbo.SUBSTRING_INDEX(#EmpId, N'|', 7) AS Result;e
Here in Result only string1 is showing and only first row.
Spend time for you and happy that come with solution , I have modify the your function with own logic you can try this, This is table value function i.e this function will return Table
CREATE FUNCTION dbo.SUBSTRING_INDEX
(
#str NVARCHAR(4000),
#delim NVARCHAR(1),
#count INT
)RETURNS #rtnTable TABLE
(
FirstString NVARCHAR(2000),
SecondString NVARCHAR(2000)
)
AS
BEGIN
DECLARE #cnt INT=1;
DECLARE #subStringPoint INT = 0
WHILE #cnt <=#count
BEGIN
SET #subStringPoint=CHARINDEX(#delim,#str,#subStringPoint)+1
SET #cnt=#cnt+1
END
INSERT INTO #rtnTable
SELECT SUBSTRING(#str,0,#subStringPoint-1) ,SUBSTRING(#str,#subStringPoint+1,LEN(#str))
RETURN
END
To call this function
DECLARE #s varchar(MAX)='45 | 00055 | 9/30/2016 | Vodafone | Randy Singh | Newyork | Test Msg | TBL101 | PC | 1.00 | COMP101 | CS | 1.00'
SELECT * FROM dbo.SUBSTRING_INDEX (#s,'|',7)
This will gives two column output
45 | 00055 | 9/30/2016 | Vodafone | Randy Singh | Newyork | Test Msg TBL101 | PC | 1.00 | COMP101 | CS | 1.00
Finally got a solution almost identical to #JaydipJ. I thought to implement in a different way but the following should do using While loop:
DECLARE #str VARCHAR(1000),
#str1 VARCHAR(1000),
#str2 VARCHAR(1000),
#pos INT,
#counter INT
SET #str = '45 | 00055 | 9/30/2016 | Vodafone | Randy Singh | Newyork | Test Msg | TBL101 | PC | 1.00 | COMP101 | CS | 1.00.............. etc'
SET #counter = 0
SET #pos = 0
WHILE #counter < 7
BEGIN
SET #pos = CHARINDEX('|', #str, #pos + 1) ---- Gets the position of delimiter '|'
SET #counter = #counter + 1 ---- Increments the counter on the given counter value
END
SET #str1 = SUBSTRING(#str, 1, #pos) ---- Splits the string on the 7th position of delimiter '|'
SET #str2 = SUBSTRING(#str, #pos + 1, LEN(#str) - #pos) ---- Splits the rest of the string
Print 'str1='+ #str1
Print 'str2='+ #str2
SELECT #str1 AS String1, #str2 AS String2
The While loop is used to iterate through the string and getting the Delimiter position, it splits the string.

How to prepend column names into column values?

Having the following table:
| Some Table |
| Id | Name | Age |
| 23 | Marc | 41 |
| 54 | Edu | 34 |
I want to get:
|Another Table's Column| Id | Name | Age |
| ..... | Id#23 | Name#Marc | Age#41 |
| ..... | Id#54 | Name#Edu | Age#34 |
This query will be used inside a dynamic sql, the name of the table is going to have passed as a parameter.
The final query must show data of at least two tables, and only one of them must show data with his column names as a prefix.
Not sure I understand what you want, but here is the script to show column names within the result for any passed table:
DECLARE #t NVARCHAR(128) = '[Person].[Person]';
DECLARE #l NVARCHAR(2000) = '';
DECLARE #s NVARCHAR(4000) = '';
SELECT #l = (
SELECT ' ''#' + Name + ''' + CAST([' + Name + '] as VARCHAR(max)) as [' + Name + '],'
FROM sys.columns
WHERE object_id = OBJECT_ID(#t)
ORDER BY column_id
FOR XML PATH(''));
SELECT #s = 'SELECT ' + LEFT(#l,LEN(#l)-1) + ' FROM ' + #t + ';'
PRINT #s
EXEC (#s)
You can tune it to link another table or many tables

SQL Server Pivot is inserting NULL values

Please find the table (OututTable) that needs to be transposed. Here the QuestionID is formed by concatenating two values -[Question:AnswerID]
refID | SessionID | QuestionID | AnswerValue
9000 | 205545715 | [4907] | Good morning
12251 | 205543469 | [10576:16307] | 3
12255 | 205543469 | [10907:17001] | 4
13157 | 205543703 | [10576:16307] | 3
14387 | 205543493 | [10907:17001] | 2
14389 | 205543493 | [10911:17007] | 3
The expected output should have one row per SessionID and the number of columns are dynamic
SessionID | [4097] | [10576:16307] | [10907:17001] | [10911:17007]
205545715 |Good morning | | |
205543469 | | 3 | 4 |
205543703 | | 3 | |
205543493 | | | 2 | 3
I have the output in the above format but there are only NULL values inserted instead of Answer values
I am thinking there might a mismatch in column names. Any help would be great! please let me know.
Code:
set #Questions = (STUFF((SELECT distinct ',[' + cast(i.SessionID as varchar(20)) + ']'
FROM OutputTable i
FOR XML PATH(''), TYPE).value('.','VARCHAR(max)'), 1, 1, ''))
print #Questions
set #SQLQuery = 'select QuestionID,'+ #Questions +' from '+'('+ 'select SessionID,QuestionID,AnswerValue from OutputTable '+ ') p '+ 'PIVOT'+ '('+'max(Answervalue)'+'FOR p.SessionID IN ('+ #Questions +')' +') as pvt'
Great Question! The problem is with the brackets in the QuestionID. While these are necessary for the Pivot Column Aliases, these don't work as string filters.
The code sample also switches QuestionID and SessionID for the expected output.
This code will return the expected output, sorted slightly differently. A temp table is created here to simulate the OutputTable object. This will need to be switched out with the DB Table.
declare
#Questions varchar(max),
#SQLQuery varchar(max)
create table #OutputTable
(
refID int,
SessionID int,
QuestionID varchar(50),
AnswerValue varchar(50)
)
insert into #OutputTable
values
(9000,205545715,'[4907]','Good morning'),
(12251,205543469,'[10576:16307]','3'),
(12255,205543469,'[10907:17001]','4'),
(13157,205543703,'[10576:16307]','3'),
(14387,205543493,'[10907:17001]','2'),
(14389,205543493,'[10911:17007]','3')
set #Questions = (STUFF((SELECT distinct ',' + cast(i.QuestionID as varchar(20))
FROM #OutputTable i
FOR XML PATH(''), TYPE).value('.','VARCHAR(max)'), 1, 1, ''))
set #SQLQuery = '
select SessionID,'+ #Questions +'
from (
select
SessionID,
replace(replace(QuestionID,''['',''''),'']'','''') QuestionID,
AnswerValue
from #OutputTable
) p
PIVOT (
max(Answervalue)
FOR p.QuestionID IN ('+ #Questions +')
) as pvt
order by SessionID desc'
exec(#SQLQuery)