Query to split String value into multiple rows - sql

i have
id | dvr
1 | 1,2,3
2 | 1,3,4
3 | 1,5,6,7,8
and would like to have
id | dvr
1 | 1
1 | 2
1 | 3
2 | 1
2 | 3
2 | 4
... and so on
what is the fastest query i should use?

Make a sql function as below:
create Function [dbo].[fun_CSVToTable]
(
#LIST varchar(7000),
#Delimeter varchar(10)
)
RETURNS #RET1 TABLE (RESULT BIGINT)
AS
BEGIN
DECLARE #RET TABLE(RESULT BIGINT)
IF LTRIM(RTRIM(#LIST))='' RETURN
DECLARE #START BIGINT
DECLARE #LASTSTART BIGINT
SET #LASTSTART=0
SET #START=CHARINDEX(#Delimeter,#LIST,0)
IF #START=0
INSERT INTO #RET VALUES(SUBSTRING(#LIST,0,LEN(#LIST)+1))
WHILE(#START >0)
BEGIN
INSERT INTO #RET VALUES(SUBSTRING(#LIST,#LASTSTART,#START-#LASTSTART))
SET #LASTSTART=#START+1
SET #START=CHARINDEX(#Delimeter,#LIST,#START+1)
IF(#START=0)
INSERT INTO #RET VALUES(SUBSTRING(#LIST,#LASTSTART,LEN(#LIST)+1))
END
INSERT INTO #RET1 SELECT * FROM #RET
RETURN
END

If you are running postgresql and dvr column is text you could do:
select
id,
unnest(string_to_array(dvr,','))
from your_table;

Related

SQL Concatenate Strings in order of line numbers

I am using SQL Server 2014 Standard.
I have the following query...
SELECT ach.amt, ades.dsline, ades.des
FROM ##ACHTrans ach
LEFT OUTER JOIN apvodes ades on 1=1 and ades.vo_id = ach.vo_id
WHERE ades.voline = '100'
ORDER by ach.apnum, ach.cknum, ach.vo_id, ach.amt desc
Which gives me the results...
+------------+---------------+------------------------------+
| ach.amt | ades.dsline | ades.des |
+------------+---------------+------------------------------+
| 1232.50 | 1 | This is the description for |
| 1232.50 | 2 | The $1,232.50 ACH Amount |
| 245.18 | 1 | This one is for the $245.18 |
| 245.18 | 2 | transactions details |
| 245.18 | 3 | that has four lines of info |
| 245.18 | 4 | in the description. |
| 79.25 | 1 | This $79.25 item has 1 line. |
| 15.00 | 1 | So does this $15.00 one. |
+------------+---------------+------------------------------+
I need a way to snag this info by the ach.amt line, and concatenate the ades.des info for results similar to:
+------------+--------------------------------------------------------------------------------------------------+
| Amount | Description |
+------------+--------------------------------------------------------------------------------------------------+
| 1232.50 | This is the description for The $1,232.50 ACH Amount |
| 245.18 | This one is for the $245.18 transactions details that has four lines of info in the description. |
| 79.25 | This $79.25 item has 1 line. |
| 15.00 | So does this $15.00 one. |
+------------+--------------------------------------------------------------------------------------------------+
This is what string_agg() does:
select ach.amt,
string_agg(des, ',') within group (order by dsline)
from t
group by ach.amt;
Without STRING_AGG you would use for XML PATH like so:
DECLARE #table TABLE (amt MONEY, dsline INT, [des] VARCHAR(1000));
INSERT #table VALUES
(1232.50,1,'This is the description for'),
(1232.50,2,'The $1,232.50 ACH Amount'),
( 245.18,1,'This one is for the $245.18'),
( 245.18,2,'transactions details'),
( 245.18,3,'that has four lines of info'),
( 245.18,4,'in the description.'),
( 79.25,1,'This $79.25 item has 1 line.'),
( 15.00,1,'So does this $15.00 one.');
SELECT
amt,
[Description] =
(
SELECT t2.[des]+''
FROM #table AS t2
WHERE t.amt = t2.amt
ORDER BY t2.dsline
FOR XML PATH('')
)
-- string_agg(des, ',') within group (order by dsline)
FROM #table AS t
GROUP BY amt;
Results:
amt Description
--------------------- ---------------------------------------------------------------------------------------------
15.00 So does this $15.00 one.
79.25 This $79.25 item has 1 line.
245.18 This one is for the $245.18transactions detailsthat has four lines of infoin the description.
1232.50 This is the description forThe $1,232.50 ACH Amount
This may not be the prettiest solution but I have had to deal with something similar and used a cursor to concatenate my strings in a temporary table and then used that in my final join statement back to the original table. I used table variables so you can play with it yourself.
Following is a code example you can play with:
declare #tableAmt table (
IDNum int,
Amt Money
)
declare #tableDesc table (
IDNum int,
LineNum int,
Info varchar(10)
)
set nocount on
insert #tableAmt (IDNum, Amt)
values (1,100.00),
(2,125.00)
insert #tableDesc (IDNum, LineNum, Info)
values (1,1,'some text'),
(1,2,'more text'),
(2,1,'different'),
(2,2,'text'),
(2,3,'final')
declare #description table
(IDNum int,
ConcatDesc varchar(30)
)
declare #id int,
#oldid int,
#string char(10),
#finalstring varchar(30)
declare getdata_cursor cursor for
select IDNum, Info
from #tableDesc
order by IDNum, LineNum
open getdata_cursor
fetch next from getdata_cursor into
#id, #string
while ##FETCH_STATUS=0
begin
if #oldid <> #id
begin
insert #description(IDNum, ConcatDesc)
values(#oldid, #finalstring)
select #finalstring = ''
end
select #finalstring = isnull(#finalstring,'') + rtrim(#string) + ' '
select #string = '', #oldid = #id
fetch next from getdata_cursor into
#id, #string
end
insert #description(IDNum, ConcatDesc)
values(#oldid, #finalstring)
close getdata_cursor
deallocate getdata_cursor
select ta.IDNum, Amt, ConcatDesc from #tableAmt ta join #description d
on ta.IDNum = d.IDNum

Insert data from Json that has multiple rows into a table in SQL

Is there any way to insert data from Json which data stored in several rows, insert into a regular table
at first I try to use FOR JSON AUTO but it returns NULL
SELECT *
FROM OPENJSON((SELECT * FROM JsonOneColumn))
Json table is like this
| Value |
| --------------------------------------------------------------- |
| {"Fname":"Cake","Fcount":3,"FDate":"2020-02-13","Fregion":"UK"} |
| {"Fname":"Coca","Fcount":5,"FDate":"2020-02-13","Fregion":"US"} |
...
it should be like this:
| Fname | Fcount | FDate | Fregion |
| ----- | ------ | -------- | ------- |
| Cake | 3 |2020-02-13| UK |
| Coca | 5 |2020-02-13| US |
Assuming your JSON data is stored in a way similar to the following:
CREATE TABLE Logs (
_id bigint primary key identity,
log nvarchar(max)
);
ALTER TABLE Logs
ADD CONSTRAINT [Log record should be formatted as JSON]
CHECK (ISJSON(log)=1)
INSERT INTO Logs
VALUES
('{"Fname":"Cake","Fcount":3,"FDate":"2020-02-13","Fregion":"UK"}'),
('{"Fname":"Coca","Fcount":5,"FDate":"2020-02-13","Fregion":"US"}')
you can query the data like this:
SELECT
JSON_VALUE(log, '$.Fname') AS Fname
, CAST(JSON_VALUE(log, '$.Fcount') AS INT) AS Fcount
, CAST(JSON_VALUE(log, '$.FDate') AS DATETIME) AS FDate
, JSON_VALUE(log, '$.Fregion') AS Fregion
FROM Logs
Output:
Fname
Fcount
FDate
Fregion
Cake
3
2020-02-13T00:00:00Z
UK
Coca
5
2020-02-13T00:00:00Z
US
Demo here
I think it will be my answer:
CREATE VIEW OneColumnIntoView
AS
SELECT *
FROM OPENJSON((SELECT CONCAT('[',(STRING_AGG(VALUE,',')),']') AS JC FROM OneColumnJsonFood))
WITH (Fname NVARCHAR(50),
Fcount INT,
Datetype NVARCHAR(4000),
Fregion NVARCHAR(50)) AS WithJson
Try this one :
you need use PK table and here this name is #id you can choose another.
i don't know about your data types
---"Fname":"Cake","Fcount":3,"FDate":"2020-02-13","Fregion":"UK"
SET NOCOUNT ON;
DECLARE #id INT
Declare #table table (Fname nvarchar(50), Fcount int,FDate date,Fregion varchar(10))
DECLARE CR CURSOR FOR
SELECT [Value] FROM JsonOneColumn
OPEN CR
FETCH NEXT FROM CR
INTO #id
WHILE ##FETCH_STATUS = 0
BEGIN
Declare #test nvarchar(500) = (SELECT [Value] FROM JsonOneColumn where id = #id)
Insert into #table (Fname, Fcount,FDate,Fregion)
Select Fname, Fcount,FDate,Fregion
From OpenJSON (#test) With ( Fname nvarchar(50) '$.Fname'
,Fcount int '$.Fcount'
,FDate date '$.FDate'
,Fregion varchar(10)) '$.Fregion')
-- Get the next id.
FETCH NEXT FROM CR
INTO #id
END
CLOSE CR;
DEALLOCATE CR;
-- Just for check out put
Select * From #table

Merge two rows in SQL with only one column being different

Assuming I have a table containing the following information:
FK | Field1 | Field2
---+--------+--------
4 | 103 | 5836
4 | 103 | 5835
FK | Field1 | Field2 | Field2A
---+--------+--------+--------
4 | 103 | 5836 | 5835
Thanks
i think you really need PIVOT, but for an exact dataset in the question, you can follow an approach like below, it's kinda weird and in T-SQl:
declare #a as int
declare #b as int
declare #c as int
declare #query Varchar(Max)
set #query = 'Select 4,103'
DECLARE curs CURSOR FOR
select * From
(select 4 as a , 103 as b , 5836 as c
Union select 4 as a , 103 as b , 5835 as c) as res
OPEN curs
FETCH NEXT FROM curs
INTO #a,#b,#c
WHILE ##FETCH_STATUS = 0
BEGIN
set #query = #query + ','+cast(#c as varchar)
FETCH NEXT FROM curs
INTO #a,#b,#c
END
CLOSE curs;
DEALLOCATE curs;
EXEC(#query)

Execute stored procedure with parameter from other table

I have a procedure with one parameter, letsay #AssetID int.
I want to select a column value from another table, then use that value as the parameter for this procedure.
I've stored procedure something like this and the table has been filtered with "Where" criteria from #AssetID parameter:
declare #inspectyear as nvarchar(max), #calc as nvarchar(max), #query as nvarchar(max);
set #inspectyear = STUFF((select distinct ',' + quotename(InspectYear) from ##t2 c
for XML path(''), type).value('.','NVARCHAR(MAX)'),1,1,'')
select #calc = ', ' + quotename(Max(InspectYear)) + ' - ' + quotename(Max(InspectYear)-2)
+ ' as Calc1, ' + quotename(Max(InspectYear)) + ' - ' + quotename(min(InspectYear))
+ ' as Calc2' from #t2;
set #query =
';with data as
(
select inspectyear,
partno, Pos, number
from #t2
unpivot
(
number
for Pos in ([Pos1], [Pos2], [Pos3], [Pos4])
) unpvt
)
select * ' + #calc + ' into ##temp
from data
pivot
(
sum(number)
for inspectyear in (' + #inspectyear + ')
) pvt
order by partno';
exec sp_executesql #query = #query;
select * from ##temp;
drop table ##temp;
So I need to create another procedure, for instance:
create procedure spExecmyProc
as
begin
exec spMyProc '#AssetID' -- <-- The parameter took from other table.
go
end
The #date parameter, took from other table.
Is it possible to do that? The result should be only one result.
So far, this is what I did. It works, but the result is not on "one result". It create more than one result if the #AssetID is more than one:
declare #AssetID int;
declare cur CURSOR FOR
select distinct AssetID from myTable
open cur
fetch next from cur into #AssetID
while ##FETCH_STATUS = 0
begin
exec mySPName #AssetID
fetch next from cur into #AssetID
end
close cur
DEALLOCATE cur
Thank you.
I'm not 100% sure I understand what you're trying to achieve, but if you want to be able to be able to run some code on each value of AssetID in mytable, returning just one result for each input value, I think you could use a Scalar-valued Function. Let's pretend that the purpose of your original stored procedure was just to increment the AssetId value by 1 for simplicity - your function could be created like this:
CREATE FUNCTION fnMyFunction (#AssetId INT)
RETURNS INT
AS
BEGIN
DECLARE #return INT
SET #return = #AssetId + 1
RETURN #return
END
If you then have some values in a table:
CREATE TABLE Assets (
AssetId INT
)
INSERT INTO Assets
SELECT 1
UNION
SELECT 2
UNION
SELECT 3
UNION
SELECT 5
UNION
SELECT 7
UNION
SELECT 5
You can call your function on each value you return:
SELECT AssetId,
dbo.fnMyFunction(AssetId) AS AssetIdPlus1
FROM Assets
Which gives these results for my super simple dataset defined above:
/------------------------\
| AssetId | AssetIdPlus1 |
|---------+--------------|
| 1 | 2 |
| 2 | 3 |
| 3 | 4 |
| 5 | 6 |
| 7 | 8 |
| 5 | 6 |
\------------------------/
If you just want to get the result for each unique value of AssetId in your table, then just return the DISTINCT results:
SELECT DISTINCT
AssetId,
dbo.fnMyFunction(AssetId)
FROM Assets
which would give these results for the same dataset above (with just one row for AssetId = 5):
/------------------------\
| AssetId | AssetIdPlus1 |
|---------+--------------|
| 1 | 2 |
| 2 | 3 |
| 3 | 4 |
| 5 | 6 |
| 7 | 8 |
\------------------------/

split the accounid and dbids between braces into two columns

I have a query to split the accounid and dbids between braces separately.
example: [14801].[42]
acid scid
14801 42
but I'm not able to separate after comma ,
example :
[27784].[41],[27781].[41],[27779].[41]
need a query to split this data into rows
you need to create function to split values into rows
create function [dbo].[udf_splitstring] (#tokens varchar(max),
#delimiter varchar(5))
returns #split table (
token varchar(200) not null )
as
begin
declare #list xml
select #list = cast('<a>'
+ replace(#tokens, #delimiter, '</a><a>')
+ '</a>' as xml)
insert into #split
(token)
select ltrim(t.value('.', 'varchar(200)')) as data
from #list.nodes('/a') as x(t)
return
end
select * from into #a udf_splitstring ('[27784].[41],[27781].[41],[27779].[41]',',')
output
[27784].[41]
[27781].[41]
[27779].[41]
coming result store in one temp table
SELECT TOKEN,REPLACE(REPLACE(SUBSTRING(TOKEN,0,CHARINDEX('.',TOKEN)),'[',''),']','') AS first_id
,REPLACE(REPLACE(REVERSE(SUBSTRING(REVERSE(TOKEN),0,CHARINDEX('.',REVERSE(TOKEN)))),'[',''),']','') AS second_id
FROM #a
output
TOKEN first_id second_id
[27784].[41] 27784 41
[27781].[41] 27781 41
[27779].[41] 27779 41
Try this:
DECLARE #START_ID VARCHAR(100)='[27784].[41],[27781].[41],[27779].[41]'
DECLARE #ID VARCHAR(MAX)
DECLARE #COUNT INT
DECLARE #TEMP TABLE(C1 VARCHAR(MAX))
WHILE( CHARINDEX(',',#START_ID))>0
BEGIN
INSERT INTO #TEMP SELECT SUBSTRING(#START_ID,0,CHARINDEX(',',#START_ID))
SET #START_ID=(SELECT REPLACE(#START_ID,(SUBSTRING(#START_ID,0,CHARINDEX(',',#START_ID)+1)),''))
END
INSERT INTO #TEMP SELECT #START_ID
SELECT C1,REPLACE(REPLACE(SUBSTRING(C1,0,CHARINDEX('.',C1)),'[',''),']','') AS SOURCE_ACCOUT_ID
,REPLACE(REPLACE(REVERSE(SUBSTRING(REVERSE(C1),0,CHARINDEX('.',REVERSE(C1)))),'[',''),']','') AS SOURCE_DATABASE_ID
FROM #TEMP
select n.ids.value ('id[1]','int') as accountid
,n.ids.value ('id[2]','int') as dbid
from (select cast (replace('<r><e><id>'+replace(replace(replace(
ids,'[',''),']',''),',','</id></e><e><id>')+
'</id></e></r>','.','</id><id>') as xml) as x
from mytable
) t
cross apply x.nodes ('/r/e') n(ids)
+-----------+------+
| accountid | dbid |
+-----------+------+
| 27784 | 41 |
+-----------+------+
| 27781 | 41 |
+-----------+------+
| 27779 | 41 |
+-----------+------+
| 28021 | 30 |
+-----------+------+
| 28024 | 30 |
+-----------+------+
| 29007 | 56 |
+-----------+------+
DDL + DML for demo
create table mytable (ids varchar(1000))
insert into mytable values
('[27784].[41],[27781].[41],[27779].[41]')
, ('[28021].[30],[28024].[30]')
, ('[29007].[56]')