How to shift the value to the next row in SQL Server? - sql-server

I have a table input like
column1 | column2
--------+--------
1,2,3 | A,B,C
4,5,6 | D,E,F
I need output like this:
column1 |column2
--------+-------
1 | A
2 | B
3 | C
4 | D
5 | E
6 | F

You question is quite unclear, but my magic crystal ball tells me, that you might be looking for something like this:
DECLARE #tbl TABLE(ID INT IDENTITY,column1 VARCHAR(100),column2 VARCHAR(100))
INSERT INTO #tbl VALUES
('1,2,3','A,B,C')
,('4,5,6','D,E,F');
WITH Casted AS
(
SELECT ID
,CAST('<x>' + REPLACE(column1,',','</x><x>')+'</x>' AS XML) AS col1XML
,CAST('<x>' + REPLACE(column2,',','</x><x>')+'</x>' AS XML) AS col2XML
FROM #tbl
)
SELECT ID,col1XML.value('/x[1]','int') AS Column1,col2XML.value('/x[1]','nvarchar(max)') AS Column2
FROM Casted
UNION ALL
SELECT ID,col1XML.value('/x[2]','int') AS Column1,col2XML.value('/x[2]','nvarchar(max)') AS Column2
FROM Casted
UNION ALL
SELECT ID,col1XML.value('/x[3]','int') AS Column1,col2XML.value('/x[3]','nvarchar(max)') AS Column2
FROM Casted
ORDER BY ID,Column1
The result
ID c1 c2
1 1 A
1 2 B
1 3 C
2 4 D
2 5 E
2 6 F

Related

SQL Server split field and create addition rows with values from main row

I have a table with rows and in one field there are values like this A,B,C
Table 'Mytable':
|ID | Date | MyValue | SplitID |
|1 | 2019-12-17 | A | |
|2 | 2019-12-15 | A,B | |
|3 | 2019-12-16 | B,C | |
Result should be:
|1 | 2019-12-17 | A | 1 |
|2 | 2019-12-15 | A | 2 |
|4 | 2019-12-15 | B | 2 |
|3 | 2019-12-16 | B | 3 |
|5 | 2019-12-16 | C | 3 |
(Sorry, I could not find HOW to format a table in the Stackoverflow help)
I tried a inline table function which splits the Field Myvalue into more lines but could not pass my rows with
charindex(',',[MyValue])>0
from MyTable as input lines.
The code is this:
ALTER function [dbo].[fncSplitString](#input Varchar(max), #Splitter Varchar(99), #ID int)
returns table as
Return
with tmp (DataItem, ix, ID) as
( select LTRIM(#input) , CHARINDEX('',#Input), #ID --Recu. start, ignored val to get the types right
union all
select LTRIM(Substring(#input, ix+1,ix2-ix-1)), ix2, #ID
from (Select *, CHARINDEX(#Splitter,#Input+#Splitter,ix+1) ix2 from tmp) x where ix2<>0
) select DataItem,ID from tmp where ix<>0
Thanks for help
Michael
You can try the following query.
Create table #Temp(
Id int,
DateField Date,
MyValue Varchar(10),
SplitID int
)
CREATE FUNCTION [dbo].[SplitPra] (#Value VARCHAR(MAX), #delimiter CHAR)
RETURNS #DataResult TABLE([Position] TINYINT IDENTITY(1,1),[Value] NVARCHAR(128))
AS
BEGIN
DECLARE #XML xml = N'<r><![CDATA[' + REPLACE(#Value, #delimiter, ']]></r><r><![CDATA[') + ']]></r>'
INSERT INTO #DataResult ([Value])
SELECT RTRIM(LTRIM(T.c.value('.', 'NVARCHAR(128)')))
FROM #xml.nodes('//r') T(c)
RETURN
END
insert into #Temp Values(1, '2019-12-17', 'A', NULL),(2, '2019-12-15', 'A,B', NULL), (3, '2019-12-16', 'B,C', NULL)
Select
#Temp.Id, DateField, b.Value as MyValue, b.Id as SplitValue
from #Temp inner join (
select
Id, f.*
from
#Temp u
cross apply [dbo].[SplitPra](u.MyValue, ',') f
)b on #Temp.Id = b.Id
Drop table #Temp
This will give an output as shown below.
Id DateField MyValue SplitValue
---------------------------------
1 2019-12-17 A 1
2 2019-12-15 A 2
2 2019-12-15 B 2
3 2019-12-16 B 3
3 2019-12-16 C 3
You can find the live demo here.
I found this solution, i hope it will work for you. But i didn't use your function to solve this problem. Instead of that, i used cross apply function.You can find the query below:
-- Creating Test Table
CREATE TABLE #Test
(
ID int,
Date date,
MyValue nvarchar(max),
SplitID int
);
GO
-- Inserting data into test table
INSERT INTO #Test VALUES (1, '2019-12-17', 'A', NULL);
INSERT INTO #Test VALUES (2, '2019-12-15', 'A,B', NULL);
INSERT INTO #Test VALUES (3, '2019-12-16', 'B,C', NULL);
GO
-- Select query
SELECT
*,
(SELECT ID FROM test t1 WHERE t.Date = t1.date) AS SplitID
FROM
(
SELECT
ROW_NUMBER() OVER (ORDER BY ID) AS ID,
Date,
substring(A.value,1,
CASE WHEN charindex(',',rtrim(ltrim(A.value))) = 0 then LEN(A.value)
ELSE charindex(',',rtrim(ltrim(A.value))) -1 end) as MyValue
FROM Test
CROSS APPLY string_split (MyValue, ',') A) AS T
ORDER BY MyValue ASC;
And the result must be like that:
ID Date MyValue SplitID
1 2019-12-17 A 1
2 2019-12-15 A 2
3 2019-12-15 B 2
4 2019-12-16 B 3
5 2019-12-16 C 3

Get multiple rows from a subquery SQL

Basically I have 2 Tables, the first with the raw material amount (QT) for each serial number and the second one with how much raw material was spent (Qt_Added) on batch's production. Like this:
Table 1
+----------+------------+-----+
| Code_Raw | Serial_Raw | Qt |
+----------+------------+-----+
| 1 | 1 | 100 |
| 1 | 2 | 150 |
| 2 | 1 | 80 |
| 1 | 3 | 100 |
+----------+------------+-----+
And Table 2
+------------+----------+------------+----------+--+
| Code_Batch | Code_Raw | Serial_Raw | Qt_Added | |
+------------+----------+------------+----------+--+
| 1 | 1 | 1 | 80 | |
| 2 | 1 | 1 | 10 | |
| 3 | 1 | 2 | 150 | |
| 4 | 1 | 3 | 80 | |
+------------+----------+------------+----------+--+
I tried to do a query for a specific Code_Raw, show me how much left for each serial number, But worked only when there's a single serial_raw.
My query:
select *
from
(select
Serial_Raw,
(Select QT From Table_1 where Code_Raw = 1) - Sum(qt_added) as Total_Remaining
from
Table_2
where
Cod_Raw = 1
group by
Serial_Raw) e
where
Total_Remaining > 0
but it throws this error
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression
And I expect :
Serial_Raw Total_Remaining
-------------------------------
1 10
3 20
Is there a struct problem or another way to do this?
I'm using SQL Server 2014
Thanks guys
Try this:
DECLARE #tbl1 TABLE
( CodeRaw INT,
Serial_Raw INT,
Qty INT)
DECLARE #tbl2 TABLE
(
CodeBatch INT,
CodeRaw INT,
Serial_Raw INT,
QtyAdded INT)
INSERT INTO #tbl1 VALUES(1,1,100)
INSERT INTO #tbl1 VALUES(1,2,150)
INSERT INTO #tbl1 VALUES(2,1,80)
INSERT INTO #tbl1 VALUES(1,3,100)
INSERT INTO #tbl2 VALUES(1,1,1,80)
INSERT INTO #tbl2 VALUES(2,1,1,10)
INSERT INTO #tbl2 VALUES(3,1,2,150)
INSERT INTO #tbl2 VALUES(4,1,3,80)
--Inner table has the summary of the Quantity added with columns CodeRaw and SerialRaw. Outer table make join with inner table and just substruct with the Qty and Sum of Qty Added.
SELECT t2.Serial_Raw, t1.Qty - t2.QtyAdded AS Total_Remaining FROM #tbl1 t1
INNER JOIN (SELECT CodeRaw, Serial_Raw , SUM(QtyAdded) QtyAdded FROM #tbl2
GROUP BY CodeRaw, Serial_Raw) AS t2 ON t2.CodeRaw = t1.CodeRaw AND t1.Serial_Raw = t2.Serial_Raw
WHERE t1.Qty - t2.QtyAdded > 0
If I understood you right, this might be what you are after
declare #tbl1 table (CodeRaw INT, Serial_Raw INT, Qty INT)
declare #tbl2 table (CodeBatch INT, CodeRaw INT, Serial_Raw INT, QtyAdded INT)
insert into #tbl1 values (1,1,100), (1,2,150), (2,1,80), (1,3,100)
insert into #tbl2 values (1,1,1,80), (2,1,1,10), (3,1,2,150), (4,1,3,80)
select t2.Serial_Raw,
t3.Qty - sum(t2.QtyAdded) as Total_Remaining
from #tbl2 t2
inner join ( select t1.Serial_Raw,
t1.CodeRaw,
sum(t1.Qty) as Qty
from #tbl1 t1
group by t1.Serial_Raw, t1.CodeRaw
) t3
on t2.Serial_Raw = t3.Serial_Raw
and t2.CodeRaw = t3.CodeRaw
group by t2.Serial_Raw, t3.Qty
So in t2 we get all distinct Serial_Raw values, and sum their QtyAdded from the first table.
In t3 we get all Qty values from the 2nd table.
All we need to do is join them together and subtract
The result of this query is
Serial_Raw Total_Remaining
---------- ---------------
1 10
2 0
3 20

UPDATE query using value as condition

I have an update query which takes the form:
update myTable
set aColumn = case when t2.someValue = 3 then 1 else 0 end
from myTable
inner join anotherTable t2 on t2.id = myTable.id
Trouble is, it doesn't work.
The 'case' or t2.someValue seems to be being ignored, and every row in myTable gets the same value (0) in aColumn - including those rows where t2.someValue = 3.
Is this a problem with SQL Server, or have I done something wrong?
EDIT: My updated query, based on a comment below...
declare #myTable table (row_id int, aCol int, bCol int)
insert into #myTable
values
(1,0,0),
(2,0,0),
(3,0,0),
(4,0,0),
(5,0,0)
declare #anotherTable table (row_id int, col_id int, someValue int)
insert into #anotherTable
values
(1,1,9),
(1,2,8),
(1,3,7),
(4,3,6),
(5,5,5)
select * from #myTable
update t
set t.aCol = case when t2.col_id = 1 then t2.someValue else t.aCol end,
t.bCol = case when t2.col_id = 3 then t2.someValue else t.bCol end
from #myTable t
left join #anotherTable t2 on t2.row_id = t.row_id
where t2.col_id IN (1,2,3,5)
select * from #myTable
This doesn't update the bCol of the 1st row with a 7 which it should...
row_id aCol bCol
1 9 0
2 0 0
3 0 0
4 0 6
5 0 0
Try this -
update myTable
set aColumn = (case when t2.someValue = 3 then 1 else 0 end)
from myTable
inner join anotherTable t2 on t2.id = myTable.id
From your edit... you have a 1:Many relationship and this is why it's not happening. You have 3 rows in #anotherTable which can join to tod_id = 1 in #myTable. This is why your update isn't working correctly.
declare #myTable table (row_id int, aCol int, bCol int)
insert into #myTable
values
(1,0,0),
(2,0,0),
(3,0,0),
(4,0,0),
(5,0,0)
declare #anotherTable table (row_id int, col_id int, someValue int)
insert into #anotherTable
values
(1,1,9),
(1,2,8),
(1,3,7),
(4,3,6),
(5,5,5)
select
t.*
,t2.*
from #myTable t
left join #anotherTable t2 on t2.row_id = t.row_id
where t2.col_id IN (1,2,3,5)
+--------+------+------+--------+--------+-----------+
| row_id | aCol | bCol | row_id | col_id | someValue |
+--------+------+------+--------+--------+-----------+
| 1 | 0 | 0 | 1 | 1 | 9 | --notice the implied cross join as
| 1 | 0 | 0 | 1 | 2 | 8 | --row_id 1 is duplicated
| 1 | 0 | 0 | 1 | 3 | 7 | --3 times
| 4 | 0 | 0 | 4 | 3 | 6 |
| 5 | 0 | 0 | 5 | 5 | 5 |
+--------+------+------+--------+--------+-----------+
Ok, Given the advice above - esp. from SCSimon - I have to admit that what I was trying to do was unusual, and too much of an edge-case, for SQL (almost any flavour) to understand.
I have resorted to making the updates to the 12 individual columns as seperate little update statements.
Not as elegant, maybe, but it works without error.
I also doubt that it actually takes any longer to run than a complex query would.

SQL Server to combine multi rows into single row where col0=col1

my table :
previousid|CurrentID|Data
| 1 | 2 | Data 1
| 2 | 3 | Data 2
| 3 | 4 | Data 3
| 4 | 5 | Data 4
Result i look for :
Select .... where PreviousID=1 :
|Col0|Col1|Col2 |Col3|Col 4|Col5| Col6 | Col7 | Col8
|1 |2 |Data 1|3 |Data 2| 4 | data 3| 5 | data 4
Select .....where PreviousID=2
|Col0|Col1|Col2 |Col3|Col 4|Col5| Col6 |
|2 |3 |Data 2|4 |Data 3| 5 | data 4|
i tried to create some SQL server query to get result with no luck, please help me guys
We can do this in a few steps:
Declare and set a variable to use for our root node, and create a temporary table to store the results from our recusive query:
Insert the results from the recusive query into the temporary table
Generate and execute dynamic sql to pivot() the temporary table.
(alternate) Generate and execute dynamic sql to use conditional aggregation instead of pivot():
rextester demo: http://rextester.com/MRFZC75180
test setup:
create table t (PreviousID int, CurrentID int, Data varchar(32));
insert into t values
(1,2,'Data 1'),(2,3,'Data 2'),(3,4,'Data 3'),(4,5,'Data 4');
Declare and set a variable to use for our root node, and create a temporary table to store the results from our recusive query:
declare #PreviousId int = 2;
create table #temp (PreviousID int
, Level int
, Col varchar(32)
, Value varchar(32)
, rn int
);
Insert the results from the recusive query into the temporary table
;with cte as (
select PreviousID, CurrentID, Data, level = 0
from t
where previousId = #PreviousId
union all
select c.PreviousID, c.CurrentID, c.Data, level = p.level +1
from t c
inner join cte as p
on c.PreviousID = p.CurrentID
)
insert into #temp
select p.PreviousId, t.level, x.col, x.value
, rn = row_number() over (order by t.level, x.col)
from cte t
cross apply (
select top 1
PreviousId
from cte i
order by level
) as p (PreviousId)
cross apply (
values ('CurrentId',convert(varchar(32),CurrentId)),('Data',Data)
) as x (col,value);
results so far:
+------------+-------+-----------+--------+----+
| PreviousID | Level | Col | Value | rn |
+------------+-------+-----------+--------+----+
| 2 | 0 | CurrentId | 3 | 1 |
| 2 | 0 | Data | Data 2 | 2 |
| 2 | 1 | CurrentId | 4 | 3 |
| 2 | 1 | Data | Data 3 | 4 |
| 2 | 2 | CurrentId | 5 | 5 |
| 2 | 2 | Data | Data 4 | 6 |
+------------+-------+-----------+--------+----+
Generate and execute dynamic sql to pivot() the temporary table.
/* pivot */
declare #cols nvarchar(max);
declare #sql nvarchar(max);
select #cols = stuff((
select
', Col'+convert(nvarchar(10),rn)
from #temp
order by 1
for xml path (''), type).value('.','nvarchar(max)')
,1,1,'')
select #sql ='
select Col0=PreviousID, ' + #cols +'
from (
select PreviousID, Value, rn= ''Col''+convert(nvarchar(10),rn)
from #temp
) as t
pivot (max([Value]) for [rn] in (' + #cols +')) p'
select #sql as CodeGenerated;
exec sp_executesql #sql;
code generated:
select Col0=PreviousID, Col1, Col2, Col3, Col4, Col5, Col6
from (
select PreviousID, Value, rn= 'Col'+convert(nvarchar(10),rn)
from #temp
) as t
pivot (max([Value]) for [rn] in ( Col1, Col2, Col3, Col4, Col5, Col6)) p
returns:
+------+------+--------+------+--------+------+--------+
| Col0 | Col1 | Col2 | Col3 | Col4 | Col5 | Col6 |
+------+------+--------+------+--------+------+--------+
| 2 | 3 | Data 2 | 4 | Data 3 | 5 | Data 4 |
+------+------+--------+------+--------+------+--------+
(alternate) Generate and execute dynamic sql to use conditional aggregation instead of pivot():
/* conditional aggregation */
--declare #cols nvarchar(max);
--declare #sql nvarchar(max);
select #cols = stuff((
select
char(10)+' , '
+ 'Col'+convert(nvarchar(10),rn)
+' = max(case when rn = '+convert(nvarchar(10),rn)+' then Value end)'
from #temp
order by 1
for xml path (''), type).value('.','nvarchar(max)')
,1,0,'')
select #sql ='
select Col0 = PreviousID'+#cols+'
from #temp
group by PreviousID'
select #sql as CodeGenerated;
exec sp_executesql #sql;
code generated:
select Col0 = PreviousID
, Col1 = max(case when rn = 1 then Value end)
, Col2 = max(case when rn = 2 then Value end)
, Col3 = max(case when rn = 3 then Value end)
, Col4 = max(case when rn = 4 then Value end)
, Col5 = max(case when rn = 5 then Value end)
, Col6 = max(case when rn = 6 then Value end)
from #temp
group by PreviousID
returns:
+------+------+--------+------+--------+------+--------+
| Col0 | Col1 | Col2 | Col3 | Col4 | Col5 | Col6 |
+------+------+--------+------+--------+------+--------+
| 2 | 3 | Data 2 | 4 | Data 3 | 5 | Data 4 |
+------+------+--------+------+--------+------+--------+
I think try with concatenating column and give them alias.
For better understanding go through this link https://www.mssqltips.com/sqlservertip/2985/concatenate-sql-server-columns-into-a-string-with-concat/

How to do recursive select in PostgreSQL with array as an argument

I am trying to implement easy recursive function in PostgreSQL but I cannot finish it...
I have got table MyTable which includes columns Col1 and Col2. Data inside is like this:
Col1 | Col2
1 | 2
2 | 5
2 | 6
3 | 7
4 | 5
4 | 2
5 | 3
I would like to write a function which takes as a parameter array of Col1 f.e. (1,2) and gives me back values from Col2 like this :
1 | 2
2 | 5
2 | 6
and after that, does it again with results : (2, 5, 6)
so:
1 | 2
2 | 5
2 | 6
5 | 3
(2 is already in, and key '6' does not exist)
and again (3):
1 | 2
2 | 5
2 | 6
5 | 3
3 | 7
and for (7) nothing because value '7' does not exist in Col1.
It is an easy recursion but I have no idea how to implement it. I have got so far something like this:
with recursive aaa(params) as (
select Col1, Col2
from MyTable
where Col1 = params -- I need an array here
union all
select Col1, Col2
from aaa
)
select * from aaa;
But it of course does not work
Thanks in advance
The basic pattern for recursion is to have your base case as the first part of the union and in the second part join the recursion result to what you need to produce the next level of results. In your case it would look like this:
WITH RECURSIVE aaa(col1, col2) AS (
SELECT col1, col2 FROM mytable
WHERE col1 = ANY (ARRAY[1,2]) -- initial case based on an array
UNION -- regular union because we only want new values
SELECT child.col1, child.col2
FROM aaa, mytable AS child -- join the source table to the result
WHERE aaa.col2 = child.col1 -- the recursion condition
)
SELECT * FROM aaa;

Resources