Get count even if the condition doesn't apply - sql-server

I have two tables
1) Document: which represent a document
+----+----------+------+
| ID | Body | Type |
+----+----------+------+
| 1 | Ramesh | 1 |
| 2 | Khilan | 1 |
| 3 | kaushik | 4 |
| 4 | Chaitali | 2 |
| 5 | Hardik | 2 |
+----+----------+------+
2) Destination: which represent a party of the document
+--------+------------+--------+
| UserId | DocumentId | Status |
+--------+------------+--------+
| 6 | 3 | 4 |
| 4 | 5 | 5 |
| 89 | 2 | 0 |
| 15 | 4 | 3 |
| 89 | 1 | 0 |
+--------+------------+--------+
The status column represent a folder for the user, i want to get the count for each type for each folder, even if the folder is empty for a specifi user,
however if want them in this from,
+--------+--------+--------------+--------------+--------------+
| UserId | Status | Type 1 Count | Type 2 Count | Type 4 Count |
+--------+--------+--------------+--------------+--------------+
| 89 | 0 | 2 | 0 | 0 |
| 89 | 3 | 0 | 0 | 0 |
| 89 | 4 | 0 | 0 | 0 |
| 89 | 5 | 0 | 0 | 0 |
+--------+--------+--------------+--------------+--------------+
the issue I'm facing is I can't find a way to get the types the user does not have by join, i can get them using CASE but not in the form i want
my query is:
`SELECT dd.[Status],
SUM(CASE WHEN d.[Type] = 1 THEN 1 ELSE 0 END) AS 'Type1Count'
SUM(CASE WHEN d.[Type] = 2 THEN 1 ELSE 0 END) AS 'Type2Count'
SUM(CASE WHEN d.[Type] = 4 THEN 1 ELSE 0 END) AS 'Type4Count'
FROM [User] u LEFT JOIN [Destination] dd ON u.[Id] = dd.[UserId]
LEFT JOIN [Document] d ON dd.[DocumentId] = d.[Id]
WHERE u.[Id] = #UserId`
the result is
+--------+--------+--------------+--------------+--------------+
| UserId | Status | Type 1 Count | Type 2 Count | Type 4 Count |
+--------+--------+--------------+--------------+--------------+
| 89 | 0 | 2 | 0 | 0 |
+--------+--------+--------------+--------------+--------------+

So join all users onto a table of all statuses (I have named this Folder as per you description in the question) before you then join to Document and Destination:
SELECT u.UserId, st.Status,
SUM(CASE WHEN doc.Type = 1 THEN 1 ELSE 0 END) AS [Type 1 Count],
SUM(CASE WHEN doc.Type = 2 THEN 1 ELSE 0 END) AS [Type 2 Count],
SUM(CASE WHEN doc.Type = 4 THEN 1 ELSE 0 END) AS [Type 4 Count]
FROM User u
CROSS JOIN Folder st
LEFT OUTER JOIN Destination d
ON d.UserId = u.UserId
AND d.Status = st.Status
LEFT OUTER JOIN Document doc
ON doc.ID = d.DocumentId
GROUP BY u.UserId, st.Status
ORDER BY u.UserId

Related

SQL Server: Flag only First duplicate row

I want to flag only the first duplicate ID-VL combination in the dataset shown below. Column FirstOccurence is what I want the end result to be.
ID VL FirstOccurence
1 a 1
1 b 1
2 a 1
2 a 0
3 a 1
3 a 0
4 a 1
4 a 0
5 a 1
5 b 1
5 a 0
There is currently not a unique index available in the original table.
Is there any way to do this with for instance the LAG-functionality? I cannot find any examples online that result in the flagging of duplicates. Any suggestions are much appreciated!
Kind regards,
Igor
One method is with ROW_NUMBER() along with a CASE expression:
SELECT
ID
,VL
,CASE ROW_NUMBER() OVER(PARTITION BY ID, VL ORDER BY ID, VL) WHEN 1 THEN 1 ELSE 0 END AS FirstOccurance
FROM dbo.example
ORDER BY
ID
,VL
,FirstOccurance;
Results:
+----+----+----------------+
| ID | VL | FirstOccurance |
+----+----+----------------+
| 1 | a | 1 |
| 1 | b | 1 |
| 2 | a | 0 |
| 2 | a | 1 |
| 3 | a | 0 |
| 3 | a | 1 |
| 4 | a | 0 |
| 4 | a | 1 |
| 5 | a | 0 |
| 5 | a | 1 |
| 5 | b | 1 |
+----+----+----------------+
Note that this result order differs from your end result. If there are one or more columns present in the table that provide the same ordering as the results in you question, specify that in the ORDER BY clause instead.

Divide selected value by count(*)

I have a Microsoft SQL Server with the following tables:
Projects
BookedHours (with fk_Project = Projects.ID)
Products
ProjectsToProducts (n:m with fk_Projects = Projects.ID and fk_Products = Products.ID)
I now want to select how many hours are booked to which product per month. The problem is, that one project can have multiple products (that's why I need the n:m table).
If I do the following, it will count the hours twice if a project has two products.
SELECT
P.ID AS fk_Product, MONTH(B.Datum) AS Monat, SUM(B.Hours) AS Stunden
FROM
tbl_BookedHours AS B
INNER JOIN
tbl_Projects AS M on B.fk_Project = M.ID
INNER JOIN
tbl_ProjectProduct AS PP ON PP.fk_Project = M.ID
INNER JOIN
tbl_Products AS P ON PP.fk_Product = P.ID
WHERE
YEAR(B.Datum) = 2020
GROUP BY
P.ID, MONTH(B.Datum)
ORDER BY
P.ID, MONTH(B.Datum)
I can get the number of products for each project with this SQL:
SELECT fk_Project, COUNT(*) AS Cnt
FROM tbl_ProjectProduct
GROUP By fk_MainProject
But how can I now divide the hours for each project by its individual factor and add it all up per product and month?
I could do it in my C# program or I could use a cursor and iterate through all projects, but I think there should be an more elegant way.
Edit with sample data:
|----------------| |----------------| |------------------------------|
| tbl_Projects | | tbl_Products | | tbl_ProjectProduct |
|----------------| |----------------| |------------------------------|
| ID | Name | | ID | Name | | ID | fk_Project | fk_Product |
|----+-----------| |----+-----------| |------------------------------|
| 1 | Project 1 | | 1 | Product 1 | | 1 | 1 | 1 |
| 2 | Project 2 | | 2 | Product 2 | | 2 | 1 | 2 |
| 3 | Project 3 | | 3 | Product 3 | | 3 | 2 | 1 |
| 4 | Project 4 | | 4 | Product 4 | | 4 | 3 | 3 |
|----------------| |----------------| | 5 | 4 | 1 |
| 6 | 4 | 2 |
| 7 | 4 | 4 |
|------------------------------|
|--------------------------------------|
| tbl_BookedHours |
|--------------------------------------|
| ID | fk_Project | Hours | Date |
|--------------------------------------|
| 1 | 1 | 10 | 2020-01-15 |
| 2 | 1 | 20 | 2020-01-20 |
| 3 | 2 | 10 | 2020-01-15 |
| 4 | 3 | 30 | 2020-01-18 |
| 5 | 2 | 20 | 2020-01-20 |
| 6 | 4 | 30 | 2020-01-25 |
| 7 | 1 | 10 | 2020-02-15 |
| 8 | 1 | 20 | 2020-02-20 |
| 9 | 2 | 10 | 2020-02-15 |
| 10 | 3 | 30 | 2020-03-18 |
| 11 | 2 | 20 | 2020-03-20 |
| 12 | 4 | 30 | 2020-03-25 |
|--------------------------------------|
The Result should be:
|----------------------------|
| fk_Product | Month | Hours |
|----------------------------|
| 1 | 1 | 55 |
| 2 | 1 | 25 |
| 3 | 1 | 30 |
| 4 | 1 | 10 |
| 1 | 2 | 25 |
| 2 | 2 | 15 |
| 1 | 3 | 30 |
| 2 | 3 | 10 |
| 3 | 3 | 30 |
| 4 | 3 | 10 |
|----------------------------|
For example booking Nr. 1 has to be divided by 2 (because Project 1 has two products) and one half of amount added to Product 1 and the other to Product 2 (Both in January). Booking Nr. 4 should not be divided, because Project 3 only has one product. Booking Numer 12 for example has to be divided by 3.
So that in total the Hours in the end add up to the same total.
I hope it's clearer now.
*** EDIT 2***
DECLARE #tbl_Projects TABLE (ID INT, [Name] VARCHAR(MAX))
INSERT INTO #tbl_Projects VALUES
(1,'Project 1'),
(2,'Project 2'),
(3,'Project 3'),
(4,'Project 4')
DECLARE #tbl_Products TABLE (ID INT, [Name] VARCHAR(MAX))
INSERT INTO #tbl_Products VALUES
(1,'Product 1'),
(2,'Product 2'),
(3,'Product 3'),
(4,'Product 4')
DECLARE #tbl_ProjectProduct TABLE (ID INT, fk_Project int, fk_Product int)
INSERT INTO #tbl_ProjectProduct VALUES
(1,1,1),
(2,1,2),
(3,2,1),
(4,3,3),
(5,4,1),
(6,4,2),
(7,4,4)
DECLARE #tbl_BookedHours TABLE (ID INT, fk_Project int, Hours int, [Date] Date)
INSERT INTO #tbl_BookedHours VALUES
(1,1,10,'2020-01-15'),
(2,1,20,'2020-01-20'),
(3,2,10,'2020-01-15'),
(4,3,30,'2020-01-18'),
(5,2,20,'2020-01-20'),
(6,4,30,'2020-01-25'),
(7,1,10,'2020-02-15'),
(8,1,20,'2020-02-20'),
(9,2,10,'2020-02-15'),
(10,3,30,'2020-03-18'),
(11,2,20,'2020-03-20'),
(12,4,30,'2020-03-25')
SELECT P.ID AS fk_Product, MONTH(B.Date) AS Month, SUM(B.Hours) AS SumHours
FROM #tbl_BookedHours AS B INNER JOIN #tbl_Projects AS M on B.fk_Project = M.ID
INNER JOIN #tbl_ProjectProduct AS PP ON PP.fk_Project = M.ID
INNER JOIN #tbl_Products AS P ON PP.fk_Product = P.ID
GROUP BY P.ID,MONTH(B.Date)
ORDER BY P.ID, MONTH(B.Date)
This gives me the wrong result, because it Counts the hours for both products:
| fk_Product | Month | SumHours |
|-------------------------------|
| 1 | 1 | 90 |
| 1 | 2 | 40 |
| 1 | 3 | 50 |
| 2 | 1 | 60 |
| 2 | 2 | 30 |
| 2 | 3 | 30 |
| 3 | 1 | 30 |
| 3 | 3 | 30 |
| 4 | 1 | 30 |
| 4 | 3 | 30 |
|-------------------------------|
Consider the following query. I modified your table variables to temp tables so it was easier to debug.
;WITH CTE AS
(
SELECT fk_Project, count(fk_Product) CNT
FROM #tbl_ProjectProduct
GROUP BY fk_Project
)
,CTE2 AS
(
SELECT t1.Date, t2.fk_Project, Hours/CNT NewHours
FROM #tbl_BookedHours t1
INNER JOIN CTE t2 on t1.fk_Project = t2.fk_Project
)
SELECT t4.ID fk_Product, MONTH(date) MN, SUM(NewHours) HRS
FROM CTE2 t1
INNER JOIN #tbl_Projects t2 on t1.fk_Project = t2.id
INNER JOIN #tbl_ProjectProduct t3 on t3.fk_Project = t2.ID
INNER JOIN #tbl_Products t4 on t4.ID = t3.fk_Product
GROUP BY t4.ID,MONTH(date)

How to get the list of products launched in the latest quarter if product gets launched at different time in different regions

I have a table
/---------------------------------------\
|Region | Product | 1 | 2 | 3 | 4 |
|-------|---------|---|-----|-----|-----|
| A | ABC | 0 | 120 | 421 | 520 |
| B | ABC | 0 | 0 | 0 | 670 |
| C | DEF | 0 | 0 | 0 | 125 |
| D | PQR | 0 | 0 | 780 | 560 |
| E | PQR | 0 | 0 | 0 | 340 |
| F | XYZ | 0 | 0 | 0 | 780 |
| G | XYZ | 0 | 0 | 0 | 900 |
\---------------------------------------/
In this table, I need to find the name of products that were launched in quarter 4.
The result that query should give is DEF and XYZ
I will be grateful if someone could help
You need to group by product and aggregate (sum) the values for each quarter per product, regardless of region:
select
Product
from #table
group by Product
having sum([4]) > 0
and sum([3]) = 0
and sum([2]) = 0
and sum([1]) = 0
With sample data to illustrate:
create table #table
(
Region varchar(1),
Product varchar(3),
[1] int,
[2] int,
[3] int,
[4] int
)
insert into #table
values
('A','ABC',0,120,421,520),
('B','ABC',0,0,0,670),
('C','DEF',0,0,0,125),
('D','PQR',0,0,780,560),
('E','PQR',0,0,0,340),
('F','XYZ',0,0,0,780),
('G','XYZ',0,0,0,900)
select
Product
from #table
group by Product
having sum([4]) > 0
and sum([3]) = 0
and sum([2]) = 0
and sum([1]) = 0
drop table #table
Output:
/---------\
| Product |
|---------|
| DEF |
| XYZ |
\---------/
try this
select *
from yourTableName a
where a.field4 > 0
and a.field3 = 0
and a.field2 = 0
and a.field1 = 0
and a.product not in (select b.product
from yourTableName b
where b.field3 >0
or b.field2>0
or b.field1>0)
and if you just want the product use below
select a.product
from yourTableName a
where a.field4 > 0
and a.field3 = 0
and a.field2 = 0
and a.field1 = 0
and a.product not in (select b.product
from yourTableName b
where b.field3 >0
or b.field2>0
or b.field1>0)
here field4 as quarter 4
field3 as quarter 3 and so on.

Multi-Class to Multi-Label Transformation in MS SQL Server

I want to transform a Data set of labels to a binary representation via a SQL query, i.e. the following table:
|---------------------------|
| Example | Label |
|---------------------------|
| 1 | Health |
| 1 | Business |
| 1 | Science |
| 2 | Sports |
| 2 | Business |
|---------------------------|
Transforms into a new table:
|---------------------------|-----------|-----------|-----------|
| Example | Business | Health | Science | Sports |
|---------------------------|-----------|-----------|-----------|
| 1 | 1 | 1 | 1 | 0 |
| 2 | 1 | 0 | 0 | 1 |
|-----------|---------------|-----------|-----------|-----------|
via some SQL query. What would be said SQL query?
select example, sum(case when label='Business' then 1 else 0 end) 'Business'
,sum(case when label='Health' then 1 else 0 end) 'Health'
,sum(case when label='Science' then 1 else 0 end) 'Science'
,sum(case when label='Sports' then 1 else 0 end) 'Sports'
From MyTable
group by example

Pivot the table along with sum total

I have a table with these data
+------------+----------------+------------+
| Department | ProgressStatus | TasksCount |
+------------+----------------+------------+
| A | Completed | 1 |
| C | Completed | 4 |
| D | Completed | 1 |
| B | Pending | 8 |
| A | Pending | 10 |
| C | Pending | 12 |
| D | Pending | 2 |
| C | Progress | 4 |
+------------+----------------+------------+
I need to write a query to get these outputs (It looks like a simple pivot table).
+-------------+-----------+---------+----------+--------------+
| Departments | Completed | Pending | Progress | Total Tasks |
+-------------+-----------+---------+----------+--------------+
| A | 1 | 10 | 0 | 11 |
| B | 0 | 8 | | 8 |
| C | 4 | 12 | 4 | 20 |
| D | 1 | 2 | | 3 |
+-------------+-----------+---------+----------+--------------+
Using conditional SUM and GROUP BY
select
department,
sum(case when ProgressStatus = 'Completed' then TasksCount end) Completed,
sum(case when ProgressStatus = 'Pending' then TasksCount end) Pending,
sum(case when ProgressStatus = 'Progress' then TasksCount end) Progress,
sum(TasksCount) Total
from your_table
group by department;
BY using pivot i tried like this
SELECT Department,isnull(Completed,0) Completed,isnull([Pending],0) [Pending],isnull([Progress],0) [Progress]
,isnull(Completed,0)+isnull([Pending],0)+isnull([Progress],0) as 'total'
FROM #Table2
PIVOT ( sum([TasksCount])
for [ProgressStatus] in ([Completed], [Pending], [Progress])) AS pvt
output
Department Completed Pending Progress total
A 1 10 0 11
B 0 8 0 8
C 4 12 4 20
D 1 2 0 3

Resources