SQL Server: Merge row with the following row - sql-server

Let's say I have this table:
ColA | ColB | SortOrder
------------------------
1 | A | 1
NULL | B | 2
2 | C | 3
NULL | D | 4
3 | E | 5
NULL | F | 6
...
This structure is repeating and will always remain in this order.
My desired output is:
ColA | ColB
-----------
1 | A B
2 | C D
3 | E F
...
How can I achieve this?

Join the table to itself and concatenate the rows.
Select a.ColA, a.ColB + ' ' + b.ColB from MyTable a
inner join MyTable b on a.sortOrder = b.sortOrder-1
WHERE a.ColA is not null
SQL fiddle:
http://sqlfiddle.com/#!6/b01df/5
Also, for the sake of completeness, here it is with no joins using window functions:
select cola, lag + ' ' + colb from (
Select lag(cola,1) over (order by sortOrder asc) cola, a.colB, lag(colb,1) over (order by sortOrder asc) lag from MyTable a
)a where cola is not null

Try the following:
SELECT colA,colAB+' '+colB colB FROM
( SELECT ROW_NUMBER() OVER (ORDER BY SortOrder) idA, colA, colB colAB
FROM tbl WHERE colA > 0 ) ta INNER JOIN
( SELECT ROW_NUMBER() OVER (ORDER BY SortOrder) idB, colB
FROM tbl WHERE colA is NULL ) tb ON idB = idA
http://sqlfiddle.com/#!6/9da9b/2
I used the ROW_NUMBER() function as a safer option since the column sortOrder could theoretically have gaps in it and therefore is not safe for being used as a link column. If sortOrder is strictly without gaps, you can use it of course directly (like Philip Devine suggested).

Related

SQL Server reset sequence column

I have the following table and I would like to reset the Code column sequence. The Code column is just an INT column.
Current
ID | Code
1 | 1
2 | 2
3 | 6
4 | 10
5 | 12
Should be
ID | Code
1 | 1
2 | 2
3 | 3
4 | 4
5 | 5
You can update from a CTE (Common Table Expression) with a ROW_NUMBER
WITH CTE AS (
SELECT ID, Code
, ROW_NUMBER() OVER (ORDER BY ID) AS rn
FROM Your_Table
)
UPDATE CTE
SET Code = rn;
That will update all records in the table.
To only correct those that need correcting, an extra criteria helps.
WITH CTE AS (
SELECT ID, Code
, ROW_NUMBER() OVER (ORDER BY ID) AS rn
FROM Your_Table
)
UPDATE CTE
SET Code = rn
WHERE (Code IS NULL OR Code != rn);
You could just run an update using the window function as below
update t
set code = t1.rownum
from
[yourtable] t
inner join
(select id,row_number()over(order by id) as rownum
from [yourtable])t1
on t.id = t1.id
UPDATE table SET Code = ID WHERE 1
// you can filter with Where
Im wrong?

TSQL How to concatenate column from joined table with group by

I have 2 connected tables: Producto and Productos_ProductosRelacionados
Producto Productos_ProductosRelacionados
|id|referencia| |id|idProducto|idProductoRelacionado|
|1 | A | |1 | 1 | 2 |
|2 | B | |2 | 1 | 3 |
|3 | C | |3 | 3 | 4 |
|4 | D | |4 | 3 | 5 |
|5 | E |
I need this:
|idProducto|referencia|
| 1 | B,C |
| 2 | |
| 3 | D,E |
I have older SQL server so can not use STRING_AGG.
So far I only achieve concatenating idProductoRelacionado:
|idProducto|idProductoRelacionado|
| 1 | 2,3 |
with:
SELECT pr1.idProducto
,STUFF((
SELECT ',' + CONVERT(varchar, pr.idProductoRelacionado)
FROM [Productos_ProductosRelacionados] as pr
WHERE pr.idProducto = pr1.idProducto
FOR XML PATH('')), 1, 1, '') as RelacionadosID
FROM [dbo].[Producto] as p1
join [Productos_ProductosRelacionados] as pr1 on p1.id = pr1.idProductoRelacionado
GROUP BY pr1.idProducto
If I try the same approach to concatenate referencia column it gives me: "Column 'dbo.Producto.id' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause."
select pr1.idProducto
,STUFF((
SELECT ',' + p.referencia
FROM [dbo].[Producto] as p
WHERE p.id = p1.id
FOR XML PATH('')), 1, 1, '') as RelacionadosREF
from [dbo].[Producto] as p1
join [Productos_ProductosRelacionados] as pr1 on p1.id = pr1.idProductoRelacionado
GROUP BY pr1.idProducto
I do not understand the difference between 2 queries, why first one is working and second is not.
Simple Join should work. Check SQLFiddle
SELECT
id,
(SELECT
cast (p1.referencia as varchar(100)) + ','
FROM producto p
LEFT JOIN Productos_ProductosRelacionados pr
on pr.idProduct = p.id
LEFT JOIN producto p1
on p1.id = pr.idProductoRelacionado
WHERE p.id = src.id
FOR XML PATH('')) as referencia
FROM
producto src;
I ran into this a while back. You have to add a join to your stuff statement:
create table #prod (
id int,
ref varchar(1)
)
insert into #prod values
(1,'A'),
(2,'B'),
(3,'C'),
(4,'D'),
(5,'E')
create table #prod_rel (
id int,
pid int,
id_rel int
)
insert into #prod_rel values
(1,1,2),
(2,1,3),
(3,3,4),
(4,3,5)
select distinct
pid
,STUFF(
(SELECT ',' + c.ref FROM #prod_rel b inner join #prod c on c.id = b.id_rel where b.pid = a.pid FOR XML PATH ('')), 1, 1, ''
)
from #prod_rel a
drop table #prod
,#prod_rel
You can use recursive query method, like this:
--Create tables for example
select * into #Producto from (
select 1 id,'A' referential union all select 2,'B' union all select 3,'C' union all select 4,'D' union all select 5,'E'
) tmp;
select * into #Productos_ProductosRelacionados from (
select 1 id,1 idProducto,2 idProductoRelacionado union all select 2,1,3 union all select 3,3,4 union all select 4,3,5
) tmp;
-- Recurse query
With tmp as (
select ROW_NUMBER() over(partition by f1.ID order by f1.id, f3.referential desc) RangID,
count(*) over(partition by f1.ID order by f1.id) NbID,
f1.id, f3.referential
from #Producto f1
left outer join #Productos_ProductosRelacionados f2 on f1.id=f2.idProducto
left outer join #Producto f3 on f2.idProductoRelacionado=f3.id
),
Recurse as (
select f1.id, f1.RangID, f1.NbID, cast(f1.referential as varchar(2000)) referential, 1 rangrecur from tmp f1 where RangID=1
union all
select f1.id, f1.RangID, f1.NbID, cast(isnull(f1.referential, '') + ',' + isnull(f2.referential, '') as varchar(2000)) referential, f2.rangrecur + 1 as rangrecur
from tmp f1 inner join Recurse f2 on f1.id=f2.id and f1.RangID-1=f2.RangID
)
select ID, Referential from recurse
where NbID=rangrecur
order by ID;

Update a table using temp table with select statement for multiple rows

I have temp table in my SP and a table in my DB and I need to update the table in the DB, and as of now I am able to update the table using select statement.
But I am having multiple records in my temp table and I am able to update only my last row of the table in DB.
Below is the query I am having,
UPDATE
Table_A
SET
Table_A.col2 = Table_B.col2,
Table_A.col3 = Table_B.col3
FROM
Some_Table AS Table_A
INNER JOIN temp_Table AS Table_B ON Table_A.col1 = Table_B.col1
And DB table structures
col1 | col2 | col3
1 | India | Delhi
2 | US | NewYork
3 | UK | London
And temp table structure as below
col1 | col2 | col3
1 | US | NewYork
2 | UK | London
3 | India | Delhi
So, I need to update my table for multiple rows.
As far as I am understanding your post I think this should be the solution for that which will update properly. Hope this helps
WITH CTE1
AS ( SELECT Col1 ,
Col2 ,
ROW_NUMBER() OVER ( PARTITION BY COl1 ORDER BY Col2 ) AS rn
FROM table_1 t1
),
CTE2
AS ( SELECT Col1 ,
Col2 ,
ROW_NUMBER() OVER ( PARTITION BY COl1 ORDER BY Col2 ) AS rn
FROM table_2 t2
)
UPDATE br
SET ....
FROM Cte1 c1
INNER JOIN cte2 c2 br ON c1.Col1 = c2.Col1
AND c1.rn = c2.rn;

Get a max record for each unique column value in a table

I have a database table like this
A || B || C
------------------------------------------
1 ABC 10
1 XYZ 5
2 EFG 100
2 LMN 150
2 WER 50
3 ABC 50
3 XYZ 75
Now i want to have a result set like this,where i want to have the max value of column C for each value in column A
A || B || C
-----------------------------------------
1 ABC 10
2 LMN 150
3 XYZ 75
I have tried using distinct and max() but it did not work. like this
select distinct #table.A,#table.B,MAX(#table.C) from #table group by #table.A,#table.B
Is there a simple way to achieve this?
Using MAX() as a window function:
SELECT t.A, t.B, t.C
FROM
(
SELECT A, B, C, MAX(C) OVER (PARTITION BY A) max_C
FROM yourTable
) t
WHERE t.C = t.max_C
If you want to retrieve only a single max record for each group of A values, then you should use the method suggested by #GurV, which is the row number:
SELECT t.A, t.B, t.C
FROM
(
SELECT A, B, C, ROW_NUMBER() OVER (PARTITION BY A ORDER BY C, B DESC) row_num
FROM yourTable
) t
WHERE t.row_num = 1
Note carefully the ORDER BY C, B inside the call to ROW_NUMBER(). This will place max C records at the top of each partition, and will then also order descending by B values. Only one value will be retained though.
If you order by both C and B the combination of both may or may not give you the highest value of Column C. So I feel the below query should work for your specific requirement.
SELECT table.A, table.B, table.C
FROM
(
SELECT A, B, C, ROW_NUMBER() OVER (PARTITION BY A ORDER BY C DESC) row_num
FROM yourTable
) table
WHERE table.row_num = 1
You can use window function to do this:
select * from (select
t.*,
row_number() over (partition by A order by C desc) rn
from your_table t) t where rn = 1;
If those aren't supported, use JOIN:
select t1.*
from your_table t1
inner join (
select A, max(C) C
from your_table
group by A
) t2 on t1.A = t2.A
and t1.C = t2.C;
Just an another way with a simple Join and Group BY
Schema:
SELECT * INTO #TAB1 FROM (
SELECT 1 A, 'ABC' B , 10 C
UNION ALL
SELECT 1 , 'XYZ' , 5
UNION ALL
SELECT 2 , 'EFG' , 100
UNION ALL
SELECT 2 , 'LMN' , 150
UNION ALL
SELECT 2 , 'WER' , 50
UNION ALL
SELECT 3 , 'ABC' , 50
UNION ALL
SELECT 3 , 'XYZ' , 75
)A
Do join to sub query
SELECT C2.A,C1.B, C2.MC
FROM #TAB1 C1
INNER JOIN
(
SELECT A, MAX(C) MC
FROM #TAB1
GROUP BY A
)AS C2 ON C1.A=C2.A AND C1.C= C2.MC
And the result will be
+---+-----+-----+
| A | B | MC |
+---+-----+-----+
| 1 | ABC | 10 |
| 2 | LMN | 150 |
| 3 | XYZ | 75 |
+---+-----+-----+

Referencing outer table in an aggregate function in a subquery

I'm looking for a solution to particular query problem. I have a table Departments and table Employees designed like that:
Departments Employees
===================== ============================
ID | Name ID | Name | Surname | DeptID
--------------------- ----------------------------
1 | ADMINISTRATION 1 | X | Y | 2
2 | IT 2 | Z | Z | 1
3 | ADVERTISEMENT 3 | O | O | 1
4 | A | B | 3
I'd like to get list of all departments whose number of employees is smaller than number of employees working in Administration.
That was one of my ideas, but it did not work:
select * from Departments as Depts where Depts.ID in
(select Employees.ID from Employees group by Employees.ID
having count(Employees.ID) < count(case when Depts.Name='ADMINISTRATION' then 1 end));
Using GROUP BY and HAVING:
SELECT
d.ID, d.Name
FROM Departments d
LEFT JOIN Employees e
ON e.DeptID = d.ID
GROUP BY d.ID, d.Name
HAVING
COUNT(e.ID) < (SELECT COUNT(*) FROM Employees WHERE DeptID = 1)
Try this,
declare #Departments table (ID int, Name varchar(50))
insert into #Departments
values
(1 ,'ADMINISTRATION')
,(2 ,'IT')
,(3 ,'ADVERTISEMENT')
declare #Employees table (ID int, Name varchar(50)
,Surname varchar(50),DeptID int)
insert into #Employees
values
(1 ,'X','Y',2)
,(2 ,'Z','Z',1)
,(3 ,'O','O',1)
,(4 ,'A','B',3)
;
WITH CTE
AS (
SELECT *
,row_number() OVER (
PARTITION BY deptid ORDER BY id
) rn
FROM #Employees
WHERE deptid <> 1
)
SELECT *
FROM cte
WHERE rn < (
SELECT count(id) admincount
FROM #Employees
WHERE DeptID = 1
)

Resources