How to show a specific view from a table in MS-SQL - sql-server

I'm new to database concepts. So, i need some help in solving a particular problem.
Say i have a table named emp whose data are as below:
id | dept | doj
100 | FS | 02-04-13
100 | HST | 02-04-14
100 | ETA | 02-04-15
What i want to display is:
id | from | to | doj
100 | FS | HST | 02-04-14
100 | HST | ETA | 02-04-15
Any help would be appreciated. Thank you.
The database i'm using is MS-SQL server 13v.

You can use apply :
select t.id, t.dept as [from], t1.dept as [to], t1.doj
from table t cross apply
(select top (1) t1.*
from table t1
where t1.id = t.id and t1.doj > t.doj
order by t1.doj
) t1

With a self join to the table:
select t.id, tt.dept [from], t.dept [to], t.doj
from tablename t inner join tablename tt
on tt.id = t.id and
tt.doj = (select max(doj) from tablename where id = t.id and doj < t.doj)
See the demo

If I understand your question correctly and you want to get data from current and subsequent row in the same result set, you may use LEAD() function:
Input:
CREATE TABLE #Table (
id int,
dept varchar(10),
doj date
)
INSERT INTO #Table
(id, dept, doj)
VALUES
(100, 'FS', '2013-04-02'),
(100, 'HST', '2014-04-02'),
(100, 'ETA', '2015-04-02'),
(101, 'XTA', '2015-04-02'),
(101, 'YTA', '2015-04-02')
Statement:
SELECT *
FROM
(
SELECT
id,
dept [from],
LEAD(dept) OVER (PARTITION BY id ORDER BY id, doj) AS [to],
LEAD(doj) OVER (PARTITION BY id ORDER BY id, doj) AS [doj]
FROM #Table
) t
WHERE t.[to] IS NOT NULL
Output:
id from to doj
100 FS HST 2014-04-02
100 HST ETA 2015-04-02
101 XTA YTA 2015-04-02

Related

How to get data as a list in SQL server [duplicate]

Let's suppose I have a temporary table which looks like this:
+----+------+
| Id | Value|
+----+------+
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 2 | 1 |
| 2 | 2 |
+----+------+
And I want my table to be like this:
+----+----------+
| Id | ValueList|
+----+----------+
| 1 | 1,2,3 |
| 2 | 1,2 |
+----+----------+
So basically I need to group my values as a comma separated list.
I already tried the following:
SELECT Id, STUFF((SELECT ',' + CAST(VALUE AS varchar) FROM #MyTable FOR XML PATH('')), 1 ,1, '') AS ValueList
FROM #MyTable
GROUP BY Id
But I get something like:
+----+---------------------+
| Id | ValueList |
+----+---------------------+
| 1 | 1,1,1,1,1,1,... |
+----+---------------------+
I cant find what I am doing wrong. Could someone help with this query? Or point me to a right direction?
Thank you.
You are missing the condition inside the sub query.
SELECT t2.Id, STUFF((SELECT ',' + CAST(VALUE AS varchar) FROM #MyTable t1 where t1.Id =t2.ID FOR XML PATH('')), 1 ,1, '') AS ValueList
FROM #MyTable t2
GROUP BY t2.Id
Demo
One alternative to using GROUP BY on the Id would be to use select distinct:
SELECT DISTINCT
Id,
STUFF((SELECT ',' + CAST(t2.VALUE AS varchar)
FROM #MyTable t2
WHERE t2.Id = t1.Id
FOR XML PATH('')), 1 ,1, '') AS ValueList
FROM #MyTable t1
Demo
One can also combine a FOR XML with a CROSS APPLY (or an OUTER APPLY) for this.
Example snippet:
declare #T table (id int, value int);
insert into #T values (1,1),(1,2),(1,3),(2,1),(2,2);
select id, stuff(x.list,1,1,'') as list
from (select distinct id from #T) as t
cross apply (
select concat(',',t2.value)
from #T t2
where t2.id = t.id
for xml path('')
) x(list)
order by id;
Result:
id list
-- -----
1 1,2,3
2 1,2
And starting from MS Sql Server 2017, STRING_AGG can be used instead.
select id, string_agg(value,',') as list
from Yourtable t
group by id;
Try this :
create table #t(id int, value int)
insert into #t values
(1,1),
(1,2),
(1,3),
(2,1),
(2,2)
SELECT t2.Id,
STUFF((SELECT ',' + CAST(VALUE AS varchar) FROM #t t1 where t1.Id =t2.ID FOR XML PATH('')), 1 ,1, '') AS list
FROM #t t2
GROUP BY t2.Id
output :
Id list
--- -------
1 1,2,3
2 1,2
Simple Solution
SELECT Id, GROUP_CONCAT(Value) as ValueList FROM MyTable GROUP BY Id;
add distinct to values if required
SELECT Id, GROUP_CONCAT(DISTINCT Value) as ValueList FROM MyTable GROUP BY Id;

JOIN SUM with WHERE

I couldn't figure this out from existing kinda similar threads. I've tried left join and different sort of subqueries but no luck.
I got left join work with group by but couldn't figure out how to add where clauses, then wen't to subqueries and broke everything.
I have two tables storage and orders.
Storage has list of unique id and name
id | name
1234 | product1
1235 | product2
A123 | product3
Orders have multiple instances code, quantity and type
code | qty| type
1234 | 10 | order
1234 | 10 | quote
1234 | 10 | order
A123 | 15 | order
1235 | 13 | order
I wan't to join these tables so that I get filtered (with where) results from storage and join with summed qty where type is order.
For example filter storage where id in (1234, A123) should result:
id | name | sum qty
1234 | product1 | 20
A123 | product3 | 15
Any help appreciated!
--
Going forward, storage has products and cols. There is table prod_to_col that has productid and col_id to tie them together.
I would need to grab product codes from table prod_to_col and show total quantity for cols according to order quantity.
I tried this according to #iSR5 example:
SELECT st.id, st.name, SUM(order.qty) AS SumQty
FROM storage
JOIN prod_to_col ON st.id=prod_to_col.col_id
JOIN orders ON order.id IN (SELECT prod_id FROM prod_to_col WHERE col_id=st.id) AND type='order'
WHERE id IN (1234, A123)
GROUP BY st.id, st.name
This almost works but quantities are multiplied in some rows some are fine, can someone point where it goes wrong?
In addition to tables storage and orders above, here's example of prod_to_col and cols:
Prod_to_col
prod_id | col_id | col_qty (per product)
1235 | C101 | 2
1236 | C102 | 1
Cols
col_id | name | other data
C101 | cname1 | --
C102 | cname2 | --
Orders
prod_id | qty | type
1235 | 10 | order
1235 | 10 | order
1236 | 2 | quote
1236 | 5 | order
Storage
st.id | st.name| SumQty
C101 | cname1 | 40
C102 | cname2 | 5
I understand I need to use two different sentence to populate storage list, one for products and one for cols. The one for products works fine.
Is this is what you need ?
DECLARE #Storage TABLE(ID VARCHAR(50), name VARCHAR(250) )
DECLARE #Orders TABLE(code VARCHAR(50), qty INT, type VARCHAR(50))
INSERT INTO #Storage VALUES
('1234','product1')
, ('1235','product2')
, ('A123','product3')
INSERT INTO #Orders VALUES
('1234',10,'order')
, ('1234',10,'quote')
, ('1234',10,'order')
, ('A123',15,'order')
, ('1235',13,'order')
SELECT
s.ID
, s.name
, SUM(o.qty) TotalQty
FROM
#Storage s
JOIN #Orders o ON o.code = s.ID AND o.type = 'order'
WHERE
s.ID IN('1234','A123')
GROUP BY
s.ID
, s.name
UPDATE
You've updated your post with more logic to cover, which wasn't provided before, however, I've update it the query for you ..
DECLARE #Storage TABLE(ID VARCHAR(50), name VARCHAR(250) )
DECLARE #Orders TABLE(code VARCHAR(50), qty INT, type VARCHAR(50))
DECLARE #Prod_to_col TABLE(prod_id VARCHAR(50), col_id VARCHAR(50), col_qty INT)
DECLARE #Cols TABLE(col_id VARCHAR(50), name VARCHAR(250))
INSERT INTO #Storage VALUES
('1234','product1')
, ('1235','product2')
, ('A123','product3')
, ('1236','product3')
INSERT INTO #Orders VALUES
('1234',10,'order')
, ('1234',10,'quote')
, ('1234',10,'order')
, ('A123',15,'order')
, ('1235',10,'order')
, ('1235',10,'order')
, ('1236',2,'quote')
, ('1236',5,'order')
INSERT INTO #Prod_to_col VALUES
('1235','C101',2)
, ('1236','C102',1)
INSERT INTO #Cols VALUES
('C101','cname1')
, ('C102','cname2')
SELECT
c.col_id
, c.name
, SUM(o.qty) * MAX(ptc.col_qty) TotalQty
FROM
#Storage s
JOIN #Orders o ON o.code = s.ID AND o.type = 'order'
JOIN #Prod_to_col ptc ON ptc.prod_id = o.code
JOIN #Cols c ON c.col_id = ptc.col_id
--WHERE
-- s.ID IN('1234','A123')
GROUP BY
c.col_id
, c.name
Try to use Left Join combined with a group by
SELECT OD.CODE
,ST.PRODUCT
,SUM(quantity) as qnt
FROM ORDERS OD
LEFT JOIN STORAGE ST ON(OD.CODE = SG.CODE)
WHERE OD.type like 'order'
GROUP BY
OD.CODE
,ST.PRODUCT
You can use Having to filter
Having id in (1234, A123)
Greetings

SQL Server - join rows into comma separated list

Let's suppose I have a temporary table which looks like this:
+----+------+
| Id | Value|
+----+------+
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 2 | 1 |
| 2 | 2 |
+----+------+
And I want my table to be like this:
+----+----------+
| Id | ValueList|
+----+----------+
| 1 | 1,2,3 |
| 2 | 1,2 |
+----+----------+
So basically I need to group my values as a comma separated list.
I already tried the following:
SELECT Id, STUFF((SELECT ',' + CAST(VALUE AS varchar) FROM #MyTable FOR XML PATH('')), 1 ,1, '') AS ValueList
FROM #MyTable
GROUP BY Id
But I get something like:
+----+---------------------+
| Id | ValueList |
+----+---------------------+
| 1 | 1,1,1,1,1,1,... |
+----+---------------------+
I cant find what I am doing wrong. Could someone help with this query? Or point me to a right direction?
Thank you.
You are missing the condition inside the sub query.
SELECT t2.Id, STUFF((SELECT ',' + CAST(VALUE AS varchar) FROM #MyTable t1 where t1.Id =t2.ID FOR XML PATH('')), 1 ,1, '') AS ValueList
FROM #MyTable t2
GROUP BY t2.Id
Demo
One alternative to using GROUP BY on the Id would be to use select distinct:
SELECT DISTINCT
Id,
STUFF((SELECT ',' + CAST(t2.VALUE AS varchar)
FROM #MyTable t2
WHERE t2.Id = t1.Id
FOR XML PATH('')), 1 ,1, '') AS ValueList
FROM #MyTable t1
Demo
One can also combine a FOR XML with a CROSS APPLY (or an OUTER APPLY) for this.
Example snippet:
declare #T table (id int, value int);
insert into #T values (1,1),(1,2),(1,3),(2,1),(2,2);
select id, stuff(x.list,1,1,'') as list
from (select distinct id from #T) as t
cross apply (
select concat(',',t2.value)
from #T t2
where t2.id = t.id
for xml path('')
) x(list)
order by id;
Result:
id list
-- -----
1 1,2,3
2 1,2
And starting from MS Sql Server 2017, STRING_AGG can be used instead.
select id, string_agg(value,',') as list
from Yourtable t
group by id;
Try this :
create table #t(id int, value int)
insert into #t values
(1,1),
(1,2),
(1,3),
(2,1),
(2,2)
SELECT t2.Id,
STUFF((SELECT ',' + CAST(VALUE AS varchar) FROM #t t1 where t1.Id =t2.ID FOR XML PATH('')), 1 ,1, '') AS list
FROM #t t2
GROUP BY t2.Id
output :
Id list
--- -------
1 1,2,3
2 1,2
Simple Solution
SELECT Id, GROUP_CONCAT(Value) as ValueList FROM MyTable GROUP BY Id;
add distinct to values if required
SELECT Id, GROUP_CONCAT(DISTINCT Value) as ValueList FROM MyTable GROUP 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;

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