Get Dynamic PIVOT result executed with in EXEC() to a varchar variable - json

Below code creates a JSON output.
Note that : The values inside #Person table are generating dynamically. Once I call the stored proc which contains the below code segment, the C# code identifies the output as a JSON eventhough it looks like a VARCHAR.I need the output JSON to be a string(VARCHAR) since the C# model cannot be generated dynamically based on the values returned by #Names.
DROP TABLE #Names
DROP TABLE #PersonInfo
CREATE TABLE #Names (ID INT,Name VARCHAR(MAX))
CREATE TABLE #PersonInfo (ID INT,NameID INT,Subject VARCHAR(100),Marks INT)
INSERT INTO #Names VALUES (1,'Paul');
INSERT INTO #Names VALUES (2,'John');
INSERT INTO #Names VALUES (3,'Tayler');
INSERT INTO #PersonInfo VALUES (1,1,'Maths',95);
INSERT INTO #PersonInfo VALUES (2,2,'Science',32);
INSERT INTO #PersonInfo VALUES (3,3,'History',23);
INSERT INTO #PersonInfo VALUES (4,2,'Maths',32);
INSERT INTO #PersonInfo VALUES (5,3,'Science',60);
INSERT INTO #PersonInfo VALUES (6,1,'Music',60);
DECLARE #DynamicCols NVARCHAR(MAX) = '';
DECLARE #pvt NVARCHAR(MAX) = '';
DECLARE #SQLQuery NVARCHAR(MAX) = '';
SELECT #DynamicCols += ', SUM(' +QUOTENAME([Name])+') AS '+[Name] FROM #Names;
SET #DynamicCols = STUFF(#DynamicCols,1,1,'')
SELECT #pvt += ', ' +QUOTENAME([Name]) FROM #Names;
SET #pvt = STUFF(#pvt,1,1,'')
EXEC ('
SELECT [Subject],' + #DynamicCols+'
FROM (SELECT [NameID], [Subject], [Marks] FROM #PersonInfo) a
INNER JOIN #Names b ON a.NameID = b.ID
PIVOT
(
SUM([Marks])
FOR [Name] IN ('+ #pvt+')
) PIV
GROUP BY [Subject] FOR JSON AUTO, INCLUDE_NULL_VALUES');
Result
[{"Subject":"History","Paul":null,"John":null,"Tayler":23},{"Subject":"Maths","Paul":95,"John":32,"Tayler":null},{"Subject":"Music","Paul":60,"John":null,"Tayler":null},{"Subject":"Science","Paul":null,"John":32,"Tayler":60}]
C# Code in the DAL Layer
public string GetMarks(string partyRoleIdList)
{
return _context.Query<string>(usp_GetMarks, new { IdList }).SingleOrDefault();
}
Is there a way to convert the executed value (EXEC()) to a VARCHAR(MAX) ??

You can easily test that the result of FOR JSON is NVARCHAR(MAX) string:
exec sp_describe_first_result_set N'
SELECT *
FROM
(
VALUES(''[a'')
) DS(col)
FOR JSON AUTO
';
Yields this:
is_hidden column_ordinal name is_nullable system_type_id system_type_name max_length precision scale collation_name
0 1 JSON_F52E2B61-18A1-11d1-B105-00805F49916B 1 231 nvarchar(max) -1 0 0 SQL_Latin1_General_CP1_CI_AS
And it is string, so you can cast it to VARCHAR(MAX) or something else easily:
EXEC ('
SELECT CAST(
(
SELECT [Subject],' + #DynamicCols+'
FROM (SELECT [NameID], [Subject], [Marks] FROM #PersonInfo) a
INNER JOIN #Names b ON a.NameID = b.ID
PIVOT
(
SUM([Marks])
FOR [Name] IN ('+ #pvt+')
) PIV
GROUP BY [Subject] FOR JSON AUTO, INCLUDE_NULL_VALUES)
AS VARCHAR(400))');

Related

How to match any value of search string from a column containing multiple values separated by space in table in sql?

I have a column in table which has multiple values separated by space.
i want to return those rows which has any of the matching values from search string.
Eg:
search string= 'mumbai pune'
This need to return rows matching word 'mumbai' or 'pune' or matching both
Declare #str nvarchar(500)
SET #str='mumbai pune'
create table #tmp
(
ID int identity(1,1),
citycsv nvarchar(500)
)
insert into #tmp(citycsv)Values
('mumbai pune'),
('mumbai'),
('nagpur')
select *from #tmp t
select *from #tmp t
where t.citycsv like '%'+#str+'%'
drop table #tmp
Required Out put:
ID CityCSV
1 mumbai pune
2 mumbai
You can use a splitter function to split your search string out as a table contain the desired search keys. Then you can join your main table with the table containing the search key using the LIKE statement.
For completeness I have included an example of a string splitter function, however there are plenty of example here on SO.
Example string splitter function:
CREATE FUNCTION [dbo].[SplitString]
(
#string NVARCHAR(MAX),
#delimiter CHAR(1)
)
RETURNS #output TABLE(splitdata NVARCHAR(MAX)
)
BEGIN
DECLARE #start INT, #end INT
SELECT #start = 1, #end = CHARINDEX(#delimiter, #string)
WHILE #start < LEN(#string) + 1 BEGIN
IF #end = 0
SET #end = LEN(#string) + 1
INSERT INTO #output (splitdata)
VALUES(SUBSTRING(#string, #start, #end - #start))
SET #start = #end + 1
SET #end = CHARINDEX(#delimiter, #string, #start)
END
RETURN
END
The following query demonstrates how the string splitter function can be combined with regular expressions to get the desired result:
SELECT DISTINCT
C.ID
,C.citycsv
FROM #tmp C
INNER JOIN (
SELECT splitdata + '[ ]%' AS MatchFirstWord -- Search pattern to match the first word in the string with the target search word.
,'%[ ]' + splitdata AS MatchLastWord -- Search pattern to match the last word in the string with the target search word.
,'%[ ]' + splitdata + '[ ]%' AS MatchMiddle -- Search pattern to match any words in the middle of the string with the target search word.
,splitdata AS MatchExact -- Search pattern for exact match.
FROM dbo.SplitString(#str, ' ')
) M ON (
(C.citycsv LIKE M.MatchFirstWord) OR
(C.citycsv LIKE M.MatchLastWord) OR
(C.citycsv LIKE M.MatchMiddle) OR
(C.citycsv LIKE M.MatchExact)
)
ORDER BY C.ID
Another approach , by using ReplaceFunction
Its syntax as following:
REPLACE ( string_expression , string_pattern , string_replacement )
so we could reach the target via replacing the every space that separated the values with the next pattern
'%'' OR t.citycsv like ''%'
An example:
Declare #str nvarchar(500),
#Where nvarchar (1000),
#Query nvarchar (4000)
SET #str='mumbai pune'
create table #tmp
(
ID int identity(1,1),
citycsv nvarchar(500)
)
insert into #tmp(citycsv)Values
('mumbai pune'),
('mumbai'),
('nagpur')
select * from #tmp t
Set #Where = 'where t.citycsv like ' + '''%'+ replace (RTRIM(LTRIM(#str)), ' ', '%'' OR t.citycsv like ''%') +'%'''
Set #Query = 'select * from #tmp t ' + #Where
execute sp_executesql #Query
drop table #tmp
The Result:

Loop through a split string variable to insert rows in a stored procedure in SQL Server 2008

I am working on SQL Server 2008 to create a stored procedure that:
takes a string variable like this: '1,2,3'
splits the string using a table-valued function to get each value separately
and then inserts each value into a new row in a table
What I am trying to do is something like this:
WHILE (select vlaue FROM dbo.SplitString('1,2,3',',')) has rows
insert into TableName (col1,col2) values (col1Data, value)
I am having a hard time trying to find the right syntax for this.
I use this Table-valued function:
CREATE FUNCTION [dbo].[Split] (#sep char(1), #s varchar(512))
RETURNS table
AS
RETURN (
WITH Pieces(pn, start, stop) AS (
SELECT 1, 1, CHARINDEX(#sep, #s)
UNION ALL
SELECT pn + 1, stop + 1, CHARINDEX(#sep, #s, stop + 1)
FROM Pieces
WHERE stop > 0
)
SELECT pn,
SUBSTRING(#s, start, CASE WHEN stop > 0 THEN stop-start ELSE 512 END) AS s
FROM Pieces
)
GO
Which takes a string with a separator and returns a table with two columns the first returns a 1-based position and the second the element at that position in the string:
Usage:
SELECT * FROM dbo.Split(',', '1,2,3')
Returns:
pn s
1 1
2 2
3 3
To Insert results into a table:
INSERT INTO TableName (Col1)
SELECT S FROM dbo.Split(',', '1,2,3)
For your specific example change your syntax to be:
insert into TableName (col1,col2)
select col1Data, value FROM dbo.SplitString('1,2,3',',')
The typical INSERT INTO ... SELECT ... should do:
INSERT INTO TableName (col1,col2)
SELECT #col1Data,value FROM dbo.SplitString('1,2,3',','))
If someone else is looking for this, I was about to make a split function as several answers mentioned but noticed there's a built-in function that does this already.
string_split was added in MSSQL 2016.
INSERT INTO Project.FormDropdownAnswers (FkTableId, CreatedBy, CreatedDate)
SELECT 123, TRY_CAST(value AS INT), #username, getdate()
FROM string_split('44,45,46,47,55',',')
https://learn.microsoft.com/en-us/sql/t-sql/functions/string-split-transact-sql
CREATE TABLE tablename
(
id SMALLINT ,
value INT
)
INSERT INTO tablename ( id, value )
SELECT * FROM dbo.Split('1,2,3',',')
try this....
If need to use as variables there is 2 nice options:
Procedure MF_SPLIT
CREATE PROC [MF_SPLIT] (#ELS NVARCHAR(MAX)=NULL OUTPUT, #RET NVARCHAR(MAX)=NULL OUTPUT, #PROC NVARCHAR(MAX)=NULL) AS BEGIN
IF #ELS IS NULL BEGIN
PRINT ' #ELS
List of elements in string (OUTPUT)
#RET
Next return (OUTPUT)
#PROC
NULL = '','', content to do split
Example:
DECLARE #NAMES VARCHAR(100) = ''ERICK,DE,VATHAIRE''
DECLARE #N VARCHAR(100)
WHILE #NAMES IS NOT NULL BEGIN
EXEC MF_SPLIT #NAMES OUTPUT, #N OUTPUT
SELECT List = #NAMES, ActiveWord = #N
END'
RETURN
END
SET #PROC = ISNULL(#PROC, ',')
IF CHARINDEX(#PROC, #ELS) = 0 BEGIN
SELECT #RET = #ELS, #ELS = NULL
RETURN
END
SELECT
#RET = LEFT(#ELS, CHARINDEX(#PROC, #ELS) - 1)
, #ELS = STUFF(#ELS, 1, LEN(#RET) + 1, '')
END
Usage:
DECLARE #NAMES VARCHAR(100) = '1,2,3'
DECLARE #N VARCHAR(100)
WHILE #NAMES IS NOT NULL BEGIN
EXEC MF_SPLIT #NAMES OUTPUT, #N OUTPUT
SELECT List = #NAMES, ActiveWord = #N
END
Procedure MF_SPLIT_DO (Depends of MF_SPLIT), less sintax to use BUT the code will be in a string and use default variable "#X"
CREATE PROC MF_SPLIT_DO (#ARR NVARCHAR(MAX), #DO NVARCHAR(MAX)) AS BEGIN
--Less sintax
DECLARE #X NVARCHAR(MAX)
WHILE #ARR IS NOT NULL BEGIN
EXEC MF_SPLIT #ARR OUT, #X OUT
EXEC SP_EXECUTESQL #DO, N'#X NVARCHAR(MAX)', #X
END
END
Usage:
EXEC MF_SPLIT_DO '1,2,3', 'SELECT #X'

Insert rows into table with single ID and rows from two comma separated lists

I have two comma separated list which I know belong to a single ID, so I need to insert each corresponding value from the two comma separated list into the table with the common ID.
I have #listA, #listB
and their values would be
#listA ='BRE,CT,DIA,DEXA'
#listB ='2.00,3.00,4.00,5.00'
and I know the ID to which these values belong, say
#SpecID=1
Now what I want is to insert data into a table which contains all three columns like this
Please provide me the steps to approach this problem.
Step 1: I have created the below Function that accepts the Comma separated list items and a Delimiter. It returns the split values along with a RowNumber.
CREATE FUNCTION dbo.SplitString
(
#Delimiter VARCHAR(1),
#InputString VARCHAR(MAX)
)
RETURNS
#ListItems TABLE
(
RowNumber INT,
List VARCHAR(50)
)
AS
BEGIN
DECLARE #Position INT, #RowNumber INT = 1, #ListItem VARCHAR(MAX)
WHILE CHARINDEX(#Delimiter, #InputString) > 0
BEGIN
SELECT #Position = CHARINDEX(#Delimiter, #InputString)
SELECT #ListItem = SUBSTRING(#InputString, 1, #Position-1)
INSERT INTO #ListItems
SELECT #RowNumber,#ListItem
SELECT #InputString = SUBSTRING(#InputString, #Position+1, LEN(#InputString)-#Position)
SET #RowNumber = #RowNumber + 1
END
INSERT INTO #ListItems
Select #RowNumber,#InputString
RETURN
END
Step 2: Using the above function, I split the comma separated list items and created 2 table variables.
DECLARE #listA VARCHAR(MAX), #listB VARCHAR(MAX), #SpecID INT, #Delimiter VARCHAR(1)
SET #listA= 'BRE,CT,DIA,DEXA'
SET #listB ='2.00,3.00,4.00,5.00'
SET #SpecID = 1
SET #Delimiter = ','
DECLARE #ListItems Table
(
SpecID INT,
listA VARCHAR(50),
listB VARCHAR(50)
)
DECLARE #TableListA Table
(
RowNumber INT,
ListA VARCHAR(50)
)
DECLARE #TableListB Table
(
RowNumber INT,
ListB VARCHAR(50)
)
INSERT INTO #TableListA
SELECT * FROM SplitString(#Delimiter,#listA)
INSERT INTO #TableListB
SELECT * FROM SplitString(#Delimiter,#listB)
INSERT INTO #ListItems
SELECT
#SpecID,
A.ListA,
B.ListB
FROM #TableListA A
INNER JOIN #TableListB B ON B.RowNumber = A.RowNumber
SELECT * FROM #ListItems
Please use the SQL Fiddle to check the output: http://sqlfiddle.com/#!6/9e12b/1/0
You will have to do it like this:
1) Split using `,`
2) Have 2 arrays
3) Both need to have the same size so iterate over 1 array
4) Use the index within the loop to insert the values.

Need a qry to join a comma separated column with another table with its Id in it to find the code

I have a table with
Table name TB1
mpeFromWHId mpeToStoreList
8 16,18,24
and Table tb2 are the codes of the comma separated storeid
nlid nlcode
16 ncl
18 mcl
24 dcl
I need a query that will result in
col1 Col2
8 ncl,mcl,dcl
First you need a function to parse comma delimited string into table, you can use this (found [here])1:
CREATE FUNCTION [dbo].Split1(#input AS Varchar(4000) )
RETURNS
#Result TABLE(Value BIGINT)
AS
BEGIN
DECLARE #str VARCHAR(20)
DECLARE #ind Int
IF(#input is not null)
BEGIN
SET #ind = CharIndex(',',#input)
WHILE #ind > 0
BEGIN
SET #str = SUBSTRING(#input,1,#ind-1)
SET #input = SUBSTRING(#input,#ind+1,LEN(#input)-#ind)
INSERT INTO #Result values (#str)
SET #ind = CharIndex(',',#input)
END
SET #str = #input
INSERT INTO #Result values (#str)
END
RETURN
END
Then you can use something like this (but there are many more options off course):
declare #searchId int
set #searchId = 8
declare #tb1 table (mpeFromWHId int, mpeToStoreList varchar(100))
insert into #tb1
select 8, '16,18,24'
declare #tb2 table (nlid int, nlcode varchar(30))
insert into #tb2
select 16, 'ncl' union
select 18, 'mcl' union
select 24, 'dcl'
select stuff((
select ',' + nlcode
from #tb2
where nlid in (
select Value
from dbo.Split1((select mpeToStoreList from #tb1 where mpeFromWHId = #searchId))
)
order by nlcode
for xml path(''), type
).value('(./text())[1]','varchar(max)'), 1, 2, '')
If you don't want to create a user function, you can do like this:
;with TB1(mpeFromWHId, mpeToStoreList) AS (
select 8,'16,18,24'
)
SELECT t.mpeFromWHId,n.ID FROM (
select *,convert(XML,'<n>'+replace(mpeToStoreList,',','</n><n>')+'</n>') as x from tb1
) AS t
cross apply(select s.b.value('.','INT') as ID from t.x.nodes('n') s(b)) n
mpeFromWHId ID
8 16
8 18
8 24

How to check if a string contains certain characters and split it in SQL Server 2008 R2?

I need to check the existence of following characters in a given string:
characters =('N', 'E', 'M', 'H', 'T', 'V', 'L', 'C' )
string = 2449.555N06704.2855EM0701H071T44.098V11.764L0.372C1
And if string contains these characters, then need to split them into values as following to insert them in details_Tb:
N = 2449.7183
E = 06704.2855
T = 0701
H = 071
T = 44.098
V = 11.764
L = 0.372
C = 1
Any solution to this?
If you are looking for this result
result:
N=2449.555
E=06704.2855
H=0701
T=071
V=44.098
L=11.764
C=0.372
then try this query..
DECLARE #CHARACTERS TABLE (CHARS CHAR(1))
INSERT INTO #CHARACTERS VALUES
('N'), ('E'), ('M'), ('H'), ('T'), ('V'), ('L'), ('C' );
DECLARE #STRING VARCHAR(500);
SET #STRING= '2449.555N06704.2855EM0701H071T44.098V11.764L0.372C1';
WITH CTE AS (
SELECT CHARS+'='+CAST(REVERSE(LEFT(REVERSE(LEFT(#STRING,
CHARINDEX(CHARS,#STRING,1)-1)), PATINDEX('%[^0-9,.]%',
REVERSE(LEFT(#STRING,CHARINDEX(CHARS,#STRING,1)-1)) + 'Z')-1))
AS VARCHAR(50)) AS RESULT
FROM #CHARACTERS )
SELECT * FROM CTE WHERE LEN(RESULT)>2
EDIT1 : As per the comment, if you want to insert the these values to detail_Tb table, please check the code below:
create table detail_Tb(N float,E float,M float,
H float,T float,V float,L float,C float);
DECLARE #CHARACTERS TABLE (CHARS CHAR(1))
INSERT INTO #CHARACTERS VALUES
('N'), ('E'), ('M'), ('H'), ('T'), ('V'), ('L'), ('C' );
DECLARE #STRING VARCHAR(500);
SET #STRING= '2449.555N06704.2855EM0701H071T44.098V11.764L0.372C1';
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
;WITH CTE AS (
SELECT CHARS,CAST(REVERSE(LEFT(REVERSE(LEFT(#STRING,
CHARINDEX(CHARS,#STRING,1)-1)), PATINDEX('%[^0-9,.]%',
REVERSE(LEFT(#STRING,CHARINDEX(CHARS,#STRING,1)-1)) + 'Z')-1))
AS VARCHAR(50)) AS RESULT
FROM #CHARACTERS )
SELECT * into #tmp FROM CTE WHERE len(RESULT)>2
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(CHARS)
from #tmp
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'Insert into detail_Tb('+#cols+')
SELECT ' + #cols + ' from #tmp x
pivot
(
MAX([RESULT])
for CHARS in (' + #cols + ')
) p '
execute(#query)
Drop table #tmp