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
)
Related
I want to join 2 tables but there is no join table between them... I usually use STRING_SPLIT but in this case, I can't figure it out. Maybe I'm just tired... Could you help me please ?
CREATE TABLE ##Provider
(
id INT,
p_name VARCHAR(50),
list_id_dep VARCHAR(250)
)
CREATE TABLE ##Department
(
id INT,
d_name VARCHAR(50)
)
INSERT INTO ##Provider (id, p_name, list_id_dep) VALUES
(1, 'toto', '/10/11/12/'),
(2, 'tata', '/09/');
INSERT INTO ##Department (id, d_name) VALUES
(9, 'dep9')
,(10, 'dep10')
,(11, 'dep11')
,(12, 'dep12');
What I want is :
id | p_name | d_name
--------------------------
1 | toto | dep10
1 | toto | dep11
1 | toto | dep12
2 | tata | dep09
I've tried :
select *
from ##Provider p
inner join ##Department d on STRING_SPLIT(p.list_id_dep, '/') = ???
select *
from ##Provider p
inner join STRING_SPLIT(p.list_id_dep, '/') dep ON dep.value = ???
select *
from ##Provider p, ##Department d
where (select value from STRING_SPLIT(p.list_id_dep, '/')) = d.id
select *
from ##Provider p, ##Department d
where d.id in (select value from STRING_SPLIT(p.list_id_dep, '/'))
Maybe STRING_SPLIT is not the right way to do it...
Thanks !
You need a lateral join to unnest the string - in SQL Server, this is implented with cross apply. Then, you can bring the department table with a regular join:
select p.id, p.p_name, d.d_name
from ##provider p
cross apply string_split(p.list_id_dep, '/') x
inner join ##department d on d.id = x.value
Demo on DB Fiddle:
id | p_name | d_name
-: | :----- | :-----
1 | toto | dep10
1 | toto | dep11
1 | toto | dep12
2 | tata | dep9
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
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
Project: Classic ASP VBScript / SQL Server 2012
I have 2 tables, Products and Categories.
I want to build a SQL query that will return ONLY the categories that have MORE THAN ONE product.
The common key between the 2 tables is the column Category_id (exists in the Products table and also in the Categories table).
You try this:
SELECT *
FROM Categories C
WHERE C.Category_id IN ( SELECT P.Category_id FROM Products P GROUP BY P.Category_id HAVING COUNT(*) > 1)
You can use the having clause for this type of query, which filters your dataset after doing all the aggregates:
declare #Categories table(CategoryID int);
declare #Products table(ProdID int, CategoryID int);
insert into #Categories values(1),(2),(3);
insert into #Products values(10,1),(20,1),(30,2),(40,2),(50,3);
select c.CategoryID
,count(1) as ProductCount
from #Categories c
left join #Products p
on(c.CategoryID = p.CategoryID)
group by c.CategoryID
having count(1) > 1;
Output:
+------------+--------------+
| CategoryID | ProductCount |
+------------+--------------+
| 1 | 2 |
| 2 | 2 |
+------------+--------------+
Use Simple Group by with Having as next demo:-
create table Products (ProductID int, name varchar(50), Category_id int )
insert into Products
values
(1,'aaaa',1),
(2,'bbbb',1),
(3,'ccccc',2),
(4,'11111',2),
(5,'11111ccc',3)
create table Categories (Category_id int, name varchar(50))
insert into Categories
values
(1,'Letters'),
(2,'Numbers'),
(3,'NumbersAndLetters')
-- CategoryID = 3 has only one product ..
select p.Category_id ,count(p.ProductID) CountProducts from
Products p inner join Categories c
on p.Category_id = c.Category_id
group by p.Category_id
having count(p.ProductID) > 1
Result:-
Category_id CountProducts
1 2
2 2
Following structure exists:
CREATE TABLE rel(
entry_id int,
parent_id int
)
CREATE TABLE entries(
entry_id int,
name varchar(44)
)
Following data exists:
INSERT INTO entries VALUES (1,'user 1');
INSERT INTO entries VALUES (2,'group 2');
INSERT INTO entries VALUES (3,'group 3');
INSERT INTO entries VALUES (4,'user 4');
INSERT INTO entries VALUES (5,'user 5');
INSERT INTO rel VALUES (3,2);
INSERT INTO rel VALUES (4,2);
INSERT INTO rel VALUES (1,3);
INSERT INTO rel VALUES (5,3);
INSERT INTO rel VALUES (2,NULL);
Result should look like:
group_id| group_name | member_id | member_name | level
2 | group 2 | 4 | user 4 | 0
2 | group 2 | 1 | user 1 | 1
2 | group 2 | 5 | user 5 | 1
3 | group 3 | 1 | user 1 | 0
3 | group 3 | 5 | user 5 | 0
I already tried stuff like the following but it's not returning the results I need:
SELECT
entries.entry_id,
entries.name,
rel.parent_id,
(SELECT name FROM entries WHERE entry_id=parent_id) AS parent_name
INTO
#tmpEntries
FROM
entries, rel
WHERE
rel.entry_id = entries.entry_id
;
SELECT * FROM #tmpEntries;
WITH MyCTE
AS (
SELECT
entry_id,
name,
parent_id,
--CAST('' AS VARCHAR(44)) AS
parent_name
FROM #tmpEntries
--WHERE parent_id IS NULL
UNION ALL
SELECT
#tmpEntries.entry_id,
#tmpEntries.name,
#tmpEntries.parent_id,
--MyCTE.name AS
#tmpEntries.parent_name
FROM #tmpEntries
INNER JOIN MyCTE ON #tmpEntries.parent_id = MyCTE.entry_id
--WHERE #tmpEntries.parent_id IS NOT NULL
-- WHERE NOT EXISTS (SELECT entry_id FROM rel WHERE parent_id=#tmpEntries.entry_id)
)
SELECT DISTINCT *
FROM MyCTE
ORDER BY parent_id
;
WITH MyCTE2
AS (
SELECT
entry_id,
name,
parent_id,
--CAST('' AS VARCHAR(44)) AS
parent_name
FROM #tmpEntries
--WHERE parent_id IS NULL
UNION ALL
SELECT
#tmpEntries.entry_id,
#tmpEntries.name,
#tmpEntries.parent_id,
--MyCTE.name AS
#tmpEntries.parent_name
FROM #tmpEntries
INNER JOIN MyCTE2 ON #tmpEntries.parent_id = MyCTE2.entry_id
--WHERE #tmpEntries.parent_id IS NOT NULL
WHERE NOT EXISTS (SELECT entry_id FROM rel WHERE parent_id=#tmpEntries.entry_id)
)
SELECT DISTINCT *
FROM MyCTE2
ORDER BY parent_id
This will work under assumption that group is anything that contains one or more members i.e. empty group will be considered a simple member.
with cte_hierarchy as
(
select entry_id, parent_id, 0 as level
from rel
where parent_id is not null
union all
select h.entry_id, rel.parent_id, h.level + 1 as level
from cte_hierarchy h
inner join rel on h.parent_id = rel.entry_id
)
select
g.entry_id as group_id,
g.name as group_name,
e.entry_id as member_id,
e.name as member_name,
h.level
from cte_hierarchy h
inner join entries e on e.entry_id = h.entry_id
inner join entries g on g.entry_id = h.parent_id
where not exists (select * from rel where parent_id = h.entry_id)
order by g.entry_id, h.level, e.entry_id
The where clause excludes nested groups that would otherwise appear as members.