SQL multiple conditions CASE WHEN and or - sql-server

I have joined two tables (tbl1 and tbl2). AnimalNo is present in both tables, but it is the primary key of tbl1 and can occur multiple times in tbl2 (if the AnimalNo is associated with multiple ConditionID)
tbl1
AnimalNo | KillDate
1 | 01/01/2019
2 | 01/01/2019
3 | 01/01/2019
4 | 01/01/2020
5 | 01/01/2020
tbl2
AnimalNo | ConditionID
1 | 1
1 | 2
2 | 1
3 | 1
3 | 2
4 | 1
5 | 1
5 | 2
I would like a table where I get a count of the AnimalID which had each condition and the count of the AnimalNo which had both ConditionID 1 & 2, in the above example:
Year | N_killed | Type1 | Type2 | Both
2019 | 3 | 3 | 2 | 2
2020 | 2 | 2 | 1 | 1
I have this query which is successful at telling me the number of AnimalNo with each individual ConditionID but I would like to add the last column called Both which has a count of the AnimalNo which have both ConditionID 1 & 2
SELECT
DATEPART(year, tbl1.KillDate) AS KillYear,
COUNT(Distinct tbl1.AnimalNo) AS N_Killed,
COUNT(CASE WHEN tbl2.ConditionId =1 THEN 1 END) AS Type1,
COUNT(CASE WHEN tbl2.ConditionId =2 THEN 1 END) AS Type2
FROM tbl1 LEFT JOIN
tbl2 ON tbl1.AnimalNo = tbl2.AnimalNo
WHERE YEAR(tbl1.KillDate) >=2012
GROUP BY DATEPART(year, KillDate)
ORDER BY DATEPART(year, KillDate)

I think this is that you want
SELECT
DATEPART(year, tbl1.KillDate) AS KillYear,
COUNT(Distinct tbl1.AnimalNo) AS N_Killed,
COUNT(CASE WHEN tbl2.ConditionId =9 THEN 1 END) AS Type1a,
COUNT(CASE WHEN tbl2.ConditionId =75 THEN 1 END) AS Type1b,
COUNT(CASE WHEN tbl2.ConditionId =23 THEN 1 END) AS Type2a,
COUNT(CASE WHEN tbl2.ConditionId =81 THEN 1 END) AS Type2b,
COUNT(CASE WHEN tbl2.ConditionId IN (9,75) OR tbl2.ConditionId IN (23,81) BothTypes
FROM tbl1 LEFT JOIN
tbl2 ON tbl1.AnimalNo = tbl2.AnimalNo
WHERE YEAR(tbl1.KillDate) >=2012
GROUP BY DATEPART(year, KillDate)
ORDER BY DATEPART(year, KillDate)

Try this:
declare #tbl1 table
(
AnimalNo int
,KillDate date
)
insert into #tbl1(AnimalNo, KillDate)
select 1, '2019-01-01' union
select 2, '2019-01-01' union
select 3, '2019-01-01' union
select 4, '2020-01-01' union
select 5, '2020-01-01'
declare #tbl2 table
(
AnimalNo int
,ConditionID int
)
insert into #tbl2(AnimalNo, ConditionID)
select 1,1 union
select 1,2 union
select 2,1 union
select 3,1 union
select 3,2 union
select 4,1 union
select 5,1 union
select 5,2
select
year(k.KillDate) as [Year]
,count(distinct k.AnimalNo) as N_Killed
,sum(c.Type1)
,sum(c.Type2)
,sum(case when c.Type1 = 1 and c.Type2 = 1 then 1 else 0 end) as Both
from
#tbl1 k
JOIN
(
select
c.AnimalNo
,max(case when c.ConditionID = 1 then 1 else 0 end) as Type1
,max(case when c.ConditionID = 2 then 1 else 0 end) as Type2
from
#tbl2 c
group by c.AnimalNo
) c
on k.AnimalNo = c.AnimalNo
group by year(k.KillDate)

Related

Change output according to LAG

I have the current issue:
I'm trying to get the amount of time each of our workers have worked in a day to calculate our company's productivity. We have the time each of our workers has entered and left the building.
The rule is, sometimes our workers leaves the building to smoke or get something from the news stand outside, so we don't take that into consideration and count as if the person never left the building.
We have a cafeteria inside our building so most people don't actually leave the building to have lunch/dinner, so we just remove 1 hour from their productivity calculation, but, if they leave for more then 45 minutes, we will consider that the worker left to lunch/dinner.
I need the end result to look like this:
+----------+----------------+----------------+---------+----------+
| PersonID | IN | OUT | MINUTES | EatOut |
+----------+----------------+----------------+---------+----------+
| 1 | 20170807 08:00 | 20170807 17:25 | 465 | 1 |
+----------+----------------+----------------+---------+----------+
| 2 | 20170807 08:00 | 20170807 17:00 | 540 | 0 |
+----------+----------------+----------------+---------+----------+
My query I have so far:
DECLARE #mytable TABLE(
PersonId INT,
Situation VARCHAR(3),
SituationDtm DATETIME
);
INSERT INTO #mytable VALUES
(1, 'IN', '20170807 08:00'),
(1, 'OUT', '20170807 12:30'),
(1, 'IN', '20170807 14:00'),
(1, 'OUT', '20170807 17:15'),
(2, 'IN', '20170807 08:00'),
(2, 'OUT', '20170807 09:15'),
(2, 'IN', '20170807 09:30'),
(2, 'OUT', '20170807 17:00');
WITH CTE AS (
SELECT
[PersonId],
Situation AS 'CUR.Situation',
SituationDtm AS 'CUR.SituationDtm',
LEAD(Situation) OVER(PARTITION BY PersonId ORDER BY SituationDtm) AS 'NEXT.Situation',
LEAD(SituationDtm) OVER(PARTITION BY PersonId ORDER BY SituationDtm) AS 'NEXT.SituationDtm'
FROM
#mytable
)
SELECT
[CUR.Situation],
[CUR.SituationDtm],
[NEXT.Situation],
[NEXT.SituationDtm],
DATEDIFF(MINUTE, [CUR.SituationDtm], [NEXT.SituationDtm]) AS 'MINUTES'
FROM
CTE
Thanks in advance
You can further query as below: Since you are looking your solution in SQL Server 2008 where you do not have lead/lag you can query as below:
;With Cte as (
Select *, RowN = Row_Number() over(Partition by PersonId order by SituationDtm) from #mytable
), Cte2 as (
Select c1.*, c2.Situation as NextSituation, c2.SituationDtm as NextSituationDtm from cte c1 left join cte c2 on c1.RowN+1 = c2.RowN
and c1.PersonId = c2.PersonId
)
Select PersonId,
Min(SituationDTM) as [In],
Max(Situationdtm) as [Out],
Sum(Case when Situation = 'OUT' and NextSituation = 'IN' and datediff(mi,SituationDtm, NextSituationDTM) > 60 then 1 else 0 end) EatOut,
Sum(Case when Situation = 'OUT' and NextSituation = 'IN' and datediff(mi,SituationDtm, NextSituationDTM) > 60 then 0 else datediff(mi,SituationDtm, NextSituationDTM) end) as [minutes]
from Cte2
group by PersonId
In later versions after >= 2012 you can query as below:
Select PersonId,
Min(SituationDTM) as [In],
Max(Situationdtm) as [Out],
Sum(Case when Situation = 'OUT' and NextSituation = 'IN' and datediff(mi,SituationDtm, NextSituationDTM) > 60 then 1 else 0 end) EatOut,
Sum(Case when Situation = 'OUT' and NextSituation = 'IN' and datediff(mi,SituationDtm, NextSituationDTM) > 60 then 0 else datediff(mi,SituationDtm, NextSituationDTM) end) as [minutes]
from (
Select *, NextSituationDTM = lead(situationdtm) over (partition by personid order by situationdtm),
NextSituation = lead(Situation) over (partition by personid order by situationdtm) from #mytable
) a
group by PersonId
Output as below:
+----------+-------------------------+-------------------------+--------+---------+
| PersonId | In | Out | EatOut | minutes |
+----------+-------------------------+-------------------------+--------+---------+
| 1 | 2017-08-07 08:00:00.000 | 2017-08-07 17:15:00.000 | 1 | 465 |
| 2 | 2017-08-07 08:00:00.000 | 2017-08-07 17:00:00.000 | 0 | 540 |
+----------+-------------------------+-------------------------+--------+---------+

Rearranging a table in tsql

So I have a table with the following columns:
Type Test Min Max
-----------------------------
1 a 1 2
1 b Null Null
2 a 0 Null
2 b Null 1
Trying to get all of them like this
Type Test1 Test1 min Test1max Test2 Test2min Test2max
------------------------------------------------------------
1 a 1 2 b Null Null
2 a 0 Null b Null 1
Tried using unpivot first before I use pivot but it's still giving duplicate tests and removing null any help with this is much appreciated
Need null values to show up as well
Select type, result
(
From
Select type, min
From table t
)
Unpivot
(result for minmax in (min1)
)
Select
Thanks
using row_number() and conditional aggregation:
select
[Type]
, Test_1 = max(case when rn = 1 then Test end)
, Test_1_Min = max(case when rn = 1 then Min end)
, Test_1_Max = max(case when rn = 1 then Max end)
, Test_2 = max(case when rn = 2 then Test end)
, Test_2_Min = max(case when rn = 2 then Min end)
, Test_2_Max = max(case when rn = 2 then Max end)
from (
select *
, rn = row_number() over (partition by [Type] order by [Test])
from t
) as s
group by s.Type
rextester demo: http://rextester.com/BKL48976
returns:
+------+--------+------------+------------+--------+------------+------------+
| Type | Test_1 | Test_1_Min | Test_1_Max | Test_2 | Test_2_Min | Test_2_Max |
+------+--------+------------+------------+--------+------------+------------+
| 1 | a | 1 | 2 | b | NULL | NULL |
| 2 | a | 0 | NULL | b | NULL | 1 |
+------+--------+------------+------------+--------+------------+------------+
select *
from table t1
join table t2
on t1.type = t2.type
and t1.test < t2.test

Select rows based on count of child table

I have three entities: department, employee, and report. A department has many employees, each of whom has many reports. I want to select the one employee in each department who has the most reports. I have no idea how to even start this query. This question seems very similar, but I can't figure out how to manipulate those answers for what I want.
I have full access to the entire system, so I can make any changes necessary. In the event of a tie, it's safe to arbitrarily pick one of the results.
Department:
ID | Name
----|------
1 | DeptA
2 | DeptB
3 | DeptC
4 | DeptD
Employee:
ID | Name | DeptID
----|------|--------
1 | Joe | 1
2 | John | 1
3 | Emma | 2
4 | Jack | 3
5 | Sven | 3
6 | Axel | 4
7 | Brad | 4
8 | Jane | 4
Report:
ID | EmployeeID
----|------------
1 | 1
2 | 2
3 | 3
4 | 5
5 | 6
6 | 6
7 | 8
Desired result (assuming I queried names only):
Joe OR John (either is acceptable)
Emma
Sven
Axel
How to start this query? Well, get the information about each employee, the department, and the number of reports:
select e.name, e.deptid, count(*) as numreports
from employee e join
reports r
on e.id = r.employeeid
group by e.name, e.deptid;
Now you just want the largest count in each department. I would suggest row_number() or rank() depending on how you want to handle ties:
select er.*
from (select e.name, e.deptid, count(*) as numreports,
row_number() over (partition by e.deptid order by count(*) desc) as seqnum
from employee e join
reports r
on e.id = r.employeeid
group by e.name, e.deptid
) er
where seqnum = 1;
If you want the department name instead of number, you can join that in as well.
From your Question schema will be
SELECT * into #Department FROM(
select 1 ID,'DEPTA' NAME
UNION ALL
select 2,'DEPTB'
UNION ALL
select 3,'DEPTC'
UNION ALL
select 4,'DEPTD')TAB
SELECT * INTO #Employee FROM (
SELECT 1 ID ,'Joe' Name , 1 DeptID
UNION ALL
SELECT 2 , 'John' , 1
UNION ALL
SELECT 3 , 'Emma' ,2
UNION ALL
SELECT 4 ,'Jack' , 3
UNION ALL
SELECT 5 ,'Sven' , 3
UNION ALL
SELECT 6 , 'Axel' , 4
UNION ALL
SELECT 7 ,'Brad' , 4
UNION ALL
SELECT 8 ,'Jane' , 4)AS A
SELECT * INTO #Report FROM(
SELECT 1 ID ,1 EmployeeID
UNION ALL
SELECT 2, 2
UNION ALL
SELECT 3 ,3
UNION ALL
SELECT 4, 5
UNION ALL
SELECT 5, 6
UNION ALL
SELECT 6, 6
UNION ALL
SELECT 7, 8
UNION ALL
SELECT 8, 8
UNION ALL
SELECT 9, 8
)AS A
And you need to apply DENSE_RANK() for giving rank based on no of reports(count)
;WITH CTE AS(
select DEP.ID DEP_ID, DEP.NAME DEP,EMP.ID EMP_ID, EMP.Name EMP
,DENSE_RANK() OVER(PARTITION BY DEP.ID ORDER BY COUNT(REP.ID) DESC) REP_RANK
,COUNT(REP.ID) NO_OF_REP FROM #Department DEP
inner join #Employee emp on emp.deptid=dep.id
inner join #report rep on rep.EmployeeID=emp.id
GROUP BY DEP.ID, DEP.NAME ,EMP.ID, EMP.Name
)
SELECT DEP, EMP, NO_OF_REP FROM CTE WHERE REP_RANK=1
Here in the DEPTA Joe & John both will be picked because both are having 1 report count which is a max count in DEPTA.
And the result will be
+-------+------+-----------+
| DEP | EMP | NO_OF_REP |
+-------+------+-----------+
| DEPTA | Joe | 1 |
| DEPTA | John | 1 |
| DEPTB | Emma | 1 |
| DEPTC | Sven | 1 |
| DEPTD | Jane | 3 |
+-------+------+-----------+
Please try the below code:-
SELECT D.NAME
FROM (
SELECT C.NAME, RANK() OVER (
PARTITION BY C.DEPTID ORDER BY C.COUNTS DESC
) RNK
FROM (
SELECT EMPID, NAME, COUNT(EMPID) AS COUNTS, DEPTID
FROM DBO.REPORT AS A
JOIN DBO.EMPLO AS B ON A.EMPID = B.ID
GROUP BY EMPID, NAME, DEPTID
) AS C
) AS D
WHERE D.RNK = 1

Count and Group By for products sold

I have a table Transaction & a table product as below .
Each Transaction has multiple products.
ProductId Name
1 ABC
2 DEF
3 GHI
Each transaction can have multiple products sold.
TransactionId ProductSoldInDept1 ProductSoldinDept2 ProductSoldinDept3
1 1 null null
2 1 2 null
3 3 1 null
4 2 3 1
I am planning to generate a report and I would like to get a result something like this :
This shows the number of products sold per each department grouped by Id
Expected Result :
ProductID Department1ProdCount Department2ProdCount Department3ProdCount
1 2 1 1
2 1 1 0
3 1 1 0
I could get till here , this is a query to get the counts for one specific product
which is productid : 1
I would like to know how I could use a group by here :
select Count(CASE WHEN ProductSoldInDept1 = 1 THEN 1 END) ,
Count(CASE WHEN ProductSoldInDept2 = 1 THEN 1 END) ,
Count(CASE WHEN ProductSoldInDept3 = 1 THEN 1 END)
from Table1
SELECT
p.ProductID,
Dept1ProdCount = COUNT(CASE WHEN t.ProductSoldInDept1 = p.ProductID THEN 1 END),
Dept2ProdCount = COUNT(CASE WHEN t.ProductSoldInDept2 = p.ProductID THEN 1 END),
Dept3ProdCount = COUNT(CASE WHEN t.ProductSoldInDept3 = p.ProductID THEN 1 END)
FROM dbo.Product AS p
LEFT OUTER JOIN dbo.[Transaction] AS t
ON p.ProductID IN
(t.ProductSoldInDept1, t.ProductSoldinDept2, t.ProductSoldinDept3)
GROUP BY p.ProductID;
Result
| PRODUCTID | DEPT1PRODCOUNT | DEPT2PRODCOUNT | DEPT3PRODCOUNT |
----------------------------------------------------------------
| 1 | 2 | 1 | 1 |
| 2 | 1 | 1 | 0 |
| 3 | 1 | 1 | 0 |
See a demo
Try this
select ProductID,
v.Department1ProdCount, v.Department2ProdCount, v.Department3ProdCount
from product a
cross apply
(
select Count(CASE WHEN ProductSoldInDept1 = a.ProductID THEN 1 END) Department1ProdCount,
Count(CASE WHEN ProductSoldInDept2 = a.ProductID THEN 1 END) Department2ProdCount,
Count(CASE WHEN ProductSoldInDept3 = a.ProductID THEN 1 END) Department3ProdCount
from Table1
) v
This:
TransactionId ProductSoldInDept1 ProductSoldinDept2 ProductSoldinDept3
1 1 null null
2 1 2 null
3 3 1 null
4 2 3 1
might be better structured as this:
transid prodsold deptid
1 1 1
2 1 1
2 2 2
3 3 1
3 1 2
4 2 1
4 3 2
4 1 3
I think that would make your queries easier to write.

How Can I Order in an SQL Statement?

I have these tables: Stock, Unit, Location, Category, StockBalance
At StockBalance: there is StockID from Stock, UnitId from Unit, LocationID from Location
I save at StockBalance Table like following
StockBalanceID | StockID | UnitID | LocationID | BalanceQuantity
1 | 1 | 1 | 1 | 20
2 | 1 | 2 | 1 | 30
3 | 1 | 3 | 1 | 40
4 | 2 | 1 | 2 | 20
5 | 2 | 2 | 2 | 30
6 | 2 | 3 | 2 | 40
I would like to show on Classic ASP as :
Group By : CategoryName
Stock Name Quantity Location Name
Qty | Unit | Qty | Unit | Qty | Unit
Stock One | 20 | One | 30 | Two | 40 | Three | Location One
Stock Two | 20 | One | 30 | Two | 40 | Three | Location Two
How can I Select From StockBalance to get like above? How about my edit one?
Please help me !
PIVOT is what you want.
First some sample data setup:
create table yourTable (
StockBalanceID int, StockID int, UnitID int, LocationID int, BalanceQuantity int);
insert yourTable
select 1 , 1 , 1 , 1 , 20
union all select 2 , 1 , 2 , 1 , 30
union all select 3 , 1 , 3 , 1 , 40
union all select 4 , 2 , 1 , 2 , 20
union all select 5 , 2 , 2 , 2 , 30
union all select 6 , 2 , 3 , 2 , 40
;
Now to do the work...
select StockID, LocationID,
sum(case UnitID when 1 then BalanceQuantity end) as [Unit One],
sum(case UnitID when 2 then BalanceQuantity end) as [Unit Two],
sum(case UnitID when 3 then BalanceQuantity end) as [Unit Three]
from yourTable
group by StockID, LocationID;
Try the following
DECLARE #StockBalances TABLE(
StockBalanceID INT,
StockID INT,
UnitID INT,
LocationID INT,
BalanceQuantity FLOAT
)
DECLARE #Stock TABLE(
StockID INT,
StockName VARCHAR(10)
)
DECLARE #Unit TABLE(
UnitID INT,
UnitName VARCHAR(10)
)
DECLARE #Location TABLE(
LocationID INT,
LocationName VARCHAR(10)
)
INSERT INTO #StockBalances SELECT 1,1,1,1,20
INSERT INTO #StockBalances SELECT 2,1,2,1,30
INSERT INTO #StockBalances SELECT 3,1,3,1,40
INSERT INTO #StockBalances SELECT 4,2,1,2,20
INSERT INTO #StockBalances SELECT 5,2,2,2 ,30
INSERT INTO #StockBalances SELECT 6,2,3,2,40
INSERT INTO #Stock SELECT 1, 'Stock 1'
INSERT INTO #Stock SELECT 2, 'Stock 2'
INSERT INTO #Unit SELECT 1, 'Unit 1'
INSERT INTO #Unit SELECT 2, 'Unit 2'
INSERT INTO #Unit SELECT 3, 'Unit 3'
INSERT INTO #Location SELECT 1, 'Location 1'
INSERT INTO #Location SELECT 2, 'Location 2'
SELECT *
FROM (
SELECT s.StockName,
sb.BalanceQuantity,
u.UnitName,
l.LocationName
FROM #StockBalances sb INNER JOIN
#Stock s ON sb.StockID = s.StockID INNER JOIN
#Unit u ON sb.UnitID = u.UnitID INNER JOIN
#Location l ON sb.LocationID = l.LocationID
) t
PIVOT (SUM(BalanceQuantity) FOR UnitName IN ([Unit 1], [Unit 2], [Unit 3])) p

Resources