Grouping Data and maintaing Calculated fields - sql

I have few fields like Department, WorkLocation, Employees and Work Date
Need to get the output based on the Given Date Parameter
Department WorkLocation EmployeeID WorkDate
---------- ------------ ---------- --------
D1 L2 121 05/01/2018
D1 L1 141 05/01/2018
D2 L1 151 05/02/2018
and so on
I have tried using temp tables to store data and get from there but is not working
Department , Number of Employees worked on the given date,Number of Employees worked in Last 7 days from the given Date,Number of Employees worked from the Begining of Month to the Given Date
i.e
Department WorkLocation Count_On_Date Count_Last7Days Count_MonthToDate
---------- ----------- ------------ --------------- -----------------
D1 L1 xxx xxx xxx
D2 L1 xxx xxx xxx
D1 L2 xxx xxx xxx
D2 L2 xxx xxx xxx
Please Help
Thanks

Use the GROUP BY clause and do the conditional aggregation with case expression
select Department, WorkLocation,
sum(case when WorkDate = #givendate then 1 else 0 end) as Count_On_Date,
sum(case when WorkDate >= dateadd(day, -8, #givendate) and
WorkDate < #givendate
then 1 else 0 end) as Count_Last7Days,
sum(case when month(WorkDate) = month(#givendate)
then 1 else 0 end) as Count_MonthToDate
from table t
group by Department, WorkLocation;

This I tested, works like expected. You need to use inline counts with different criteria. To produce each line we have case clauses with one-way conditions to cancel out the unwanted values.
select
wd.Department,
wd.WorkLocation,
COUNT(1) as Count_On_Date,
COUNT(case when DATEDIFF(DAY, wd.WorkDate, GETDATE()) < 7 then 1 end) as Count_Last7Days,
COUNT(case when DATEDIFF(DAY, wd.WorkDate, GETDATE()) < 30 then 1 end) as Count_MonthToDate
from WorkData as wd
group by wd.Department, wd.WorkLocation

Related

Count average with multiple conditions

I'm trying to create a query which allows to categorize the average percentage for specific data per month.
Here's how my dataset presents itself:
Date
Name
Group
Percent
2022-01-21
name1
gr1
5.2
2022-01-22
name1
gr1
6.1
2022-01-26
name1
gr1
4.9
2022-02-01
name1
gr1
3.2
2022-02-03
name1
gr1
8.1
2022-01-22
name2
gr1
36.1
2022-01-25
name2
gr1
32.1
2022-02-10
name2
gr1
35.8
...
...
...
...
And here's what I want to obtain with my query (based on what I showed of the table):
Month
<=25%
25<_<=50%
50<_<=75%
75<_<=100%
01
1
1
0
0
02
1
1
0
0
...
...
...
...
...
The result needs to:
Be ordered by month
Have the average use for each name counted and categorized
So far I know how to get the average of the Percent value per Name:
SELECT Name,
AVG(Percent)
from `table`
where Group = 'gr1'
group by Name
and how to count iterations of Percent in the categories created for the query:
SELECT EXTRACT(MONTH FROM Date) as Month,
COUNT(CASE WHEN Percent <= 25 AND Group = 'gr1' THEN Name END) `_25`,
COUNT(CASE WHEN Percent > 25 AND Percent <= 50 AND Group = 'gr1' THEN Name END) `_50`,
COUNT(CASE WHEN Percent > 50 AND Percent <= 75 AND Group = 'gr1' THEN Name END) `_75`,
COUNT(CASE WHEN Percent > 75 AND Percent <= 100 AND Group = 'gr1' THEN Name END) `_100`,
FROM `table`
GROUP BY Month
ORDER BY Month
but this counts all iterations of every name where I want the average of those values.
I've been struggling to figure out how to combine the two queries or to create a new one that answers my need.
I'm working with the BigQuery service from Google Cloud
This query produces the needed result, based on your example. So basically this combines your 2 queries using subquery, where the subquery is responsible to calculate AVG grouped by Name, Month and Group, and the outer query is for COUNT and "categorization"
SELECT
Month,
COUNT(CASE
WHEN avg <= 25 THEN Name
END) AS _25,
COUNT(CASE
WHEN avg > 25
AND avg <= 50 THEN Name
END) AS _50,
COUNT(CASE
WHEN avg > 50
AND avg <= 75 THEN Name
END) AS _75,
COUNT(CASE
WHEN avg > 75
AND avg <= 100 THEN Name
END) AS _100
FROM
(
SELECT
EXTRACT(MONTH from Date) AS Month,
Name,
AVG(Percent) AS avg
FROM
table1
GROUP BY Month, Name, Group
HAVING Group = 'gr1'
) AS namegr
GROUP BY Month
This is the result:
Month
_25
_50
_75
_100
1
1
1
0
0
2
1
1
0
0
See also Fiddle (BUT on MySql) - http://sqlfiddle.com/#!9/16c5882/9
You can use this query to Group By Month and each Name
SELECT CONCAT(EXTRACT(MONTH FROM Date), ', ', Name) AS DateAndName,
CASE
WHEN AVG(Percent) <= 25 THEN '1'
ELSE '0'
END AS '<=25%',
CASE
WHEN AVG(Percent) > 25 AND AVG(Percent) <= 50 THEN '1'
ELSE '0'
END AS '25<_<=50%',
CASE
WHEN AVG(Percent) > 50 AND AVG(Percent) <= 75 THEN '1'
ELSE '0'
END AS '50<_<=75%',
CASE
WHEN AVG(Percent) > 75 AND AVG(Percent) <= 100 THEN '1'
ELSE '0'
END AS '75<_<=100%'
from DataTable /*change to your table name*/
group by EXTRACT(MONTH FROM Date), Name
order by DateAndName
It gives the following result:
DateAndName
<=25%
25<_<=50%
50<_<=75%
75<_<=100%
1, name1
1
0
0
0
1, name2
0
1
0
0
2, name1
1
0
0
0
2, name2
0
1
0
0

SQL/DB2 getting single row of results per employee with a UNION

I'm currently using a UNION on 2 select statements and while I'm getting the correct data, it's not exactly what I actually need when it comes time to use it in a front-end view
I'm currently using this query:
SELECT
T.employee as employee,
'Orders' as TYPE,
SUM(CASE WHEN t.order_count < QUANT THEN t.order_count ELSE QUANT END) as DATA
FROM schemaOne.order_list T
WHERE t.order_date > CURRENT_DATE - 35 DAYS
group by t.employee
UNION
select
T.employee as employee,
'Sales' as TYPE,
sum(price * quant) as DATA
from schemaOne.sales T
WHERE T.sales_date > CURRENT_DATE - 35 DAYS
group by T.employee
order by data desc;
with these dummy tables as examples and getting the following result:
order_list
employee | order_count | quant | order_date
--------------------------------------------------
123 | 5 | 1 | 2022-03-02
456 | 1 | 5 | 2022-03-02
sales
employee | price | quant | order_date
--------------------------------------------------
123 | 500 | 1 | 2022-03-02
456 | 1000 | 1 | 2022-03-02
Result
employee | type | data
------------------------------------------
123 Orders 1
123 Sales 500
456 Orders 5
456 Sales 1000
Is there a way to use a UNION but alter it so that I can instead get a single row for each employee and just get rid of the type/data columns and instead set each piece of data to the desired column (the type would instead be the column name ) like so:
Desired Result
employee | Orders | Sales
---------------------------------
123 | 1 | 500
456 | 5 | 1000
Try adding an outer query:
select employee,
MAX(case when type=Orders then data end) as orders ,
MAX(case when type=Sales then data end) as Sales
from (
SELECT T.employee as employee,
'Orders' as TYPE,
SUM(CASE WHEN t.order_count < QUANT THEN t.order_count ELSE QUANT END) as DATA
FROM schemaOne.order_list T
WHERE t.order_date > CURRENT_DATE - 35 DAYS
group by t.employee
UNION
select T.employee as employee,
'Sales' as TYPE,
sum(price * quant) as DATA
from schemaOne.sales T
WHERE T.sales_date > CURRENT_DATE - 35 DAYS
group by T.employee
) as t1
GROUP BY employee;
Note that I removed order by data desc it has no effect inside the union
You can join tables through employee columns such as
SELECT o.employee,
SUM(CASE
WHEN o.order_count < o.quant THEN
o.order_count
ELSE
o.quant
END) AS Orders,
SUM(s.price * s.quant) AS Sales
FROM schemaOne.order_list o
JOIN schemaOne.sales s
ON s.employee = o.employee
AND s.sales_date = o.order_date
WHERE o.order_date > current_date - 35 DAYS
GROUP BY o.employee

Calculating NETWORKDAYS using a CalendarTable SQL Server 2008

Previously I created a SELECT to calculate Business Days (Weekends/Holydays) which didn't work for certain date ranges for each records. I was suggested to use a Calendar Table, which actually worked for other requirements I had on hold but I can't figure how to calculate INI and END for business days.
I've tried to do a SELECT with a semi left join and a join (suggested as well) but it doesn't seem to calculate at all.
This is the Calendar Table:
CalendarId DateValue DayNumberOfWeek NameOfDay NameOfMonth WeekOfYear JulianDay USAIsBankHoliday USADayName
----------- ----------------------- --------------- ---------- ----------- ----------- ----------- ---------------- -------------------
20100101 2010-01-01 00:00:00.000 5 Viernes Enero 1 1 1 Año nuevo
My table:
INI FIN DD --DATEDIFF BETWEEN DATES
------------------------------ ------------------------------ -----------
20180129 20180211 14
My attempt at calculating business days
SELECT CONVERT(NVARCHAR,INI,112) AS INI,
CONVERT(NVARCHAR,FIN,112) AS FIN,
DATEDIFF(DD, INI, FIN)+1 AS DD
FROM HISTORICOLICMED T
WHERE EXISTS(
SELECT 1 FROM CALENDAR C
WHERE T.INI = C.CALENDARID and T.FIN = C.CALENDARID
AND C.USAISBANKHOLIDAY = 0 BETWEEN T.INI AND T.FIN)
I expect getting the correct business day calculations including holidays so for the example above I will get something like:
INI FIN DD ND
------------------------------ ------------------------------ ----- -----
20180129 20180211 14 10
Sub-Select the count of rows in the calendar table that are between INI and FIN and are business days.
SELECT CONVERT(NVARCHAR,INI,112) AS INI,
CONVERT(NVARCHAR,FIN,112) AS FIN,
DATEDIFF(DD, INI, FIN)+1 AS DD,
(SELECT COUNT(*) FROM CalendarTable WHERE ...) AS ND
FROM HISTORICOLICMED
Proposed solution:
set datefirst 1;
select INI
,FIN
, DATEDIFF(DD, INI, FIN)+1 as DD
,(DATEDIFF(DD, INI, FIN)+1)
-(DATEDIFF(WK, INI, FIN)*2)
-(case DATEPART(DW, INI) when 7 then 1 else 0 end)
-(case DATEPART(DW, FIN) when 6 then 1 else 0 end)
-(select COUNT(*) from CALENDAR
where DateValue between INI and FIN
and USAIsBankHoliday = 1)
as ND -- Working days except US Bank holidays
from HISTORICOLICMED;

SQL query to group by age range from date created

I want to get statistics with sql query. My table is like this:
ID MATERIAL CREATEDATE DEPARTMENT
1 M1 10.10.1980 D1
2 M2 11.02.1970 D2
2 M3 18.04.1971 D3
.....................
.....................
.....................
How can I get a range of data count like this
DEPARTMENT AGE<10 10<AGE<20 20<AGE
D1 24 123 324
D2 24 123 324
Assuming that CREATEDATE is a date column, in PostgreSQL you can use the AGE function:
select DEPARTMENT, age(CREATEDATE) as AGE
from Materials
and with date_part you can get the age in years. To show the data in the format that you want, you could use this GROUP BY query:
select
DEPARTMENT,
sum(case when date_part('year', age(CREATEDATE))<10 then 1 end) as "age<10",
sum(case when date_part('year', age(CREATEDATE))>=10 and date_part('year', age(CREATEDATE))<20 then 1 end) as "10<age<20",
sum(case when date_part('year', age(CREATEDATE))>=20 then 1 end) as "20<age"
from
Materials
group by
DEPARTMENT
which can be simplified as:
with mat_age as (
select DEPARTMENT, date_part('year', age(CREATEDATE)) as mage
from Materials
)
select
DEPARTMENT,
sum(case when mage<10 then 1 end) as "age<10",
sum(case when mage>=10 and mage<20 then 1 end) as "10<age<20",
sum(case when mage>=20 then 1 end) as "20<age"
from
mat_age
group by
DEPARTMENT;
if you are using PostgreSQL 9.4 you can use FILTER:
with mat_age as (
select DEPARTMENT, date_part('year', age(CREATEDATE)) as mage
from Materials
)
select
DEPARTMENT,
count(*) filter (where mage<10) as "age<10",
count(*) filter (where mage>=10 and mage<20) as "10<age<20",
count(*) filter (where mage>=20) as "20<age"
from
mat_age
group by
DEPARTMENT;
The following solution assumes that your CREATEDATE column exists as some sort of valid Postgres date type. If this be not the case, and it is being stored as text, you will first have to convert it to date in order for the query to work.
SELECT DEPARTMENT,
SUM(CASE WHEN DATEDIFF(year, CREATEDATE, now()::date) < 10 THEN 1 ELSE 0 END) AS "AGE<10",
SUM(CASE WHEN DATEDIFF(year, CREATEDATE, now()::date) >= 10 AND
DATEDIFF(year, CREATEDATE, now()::date) < 20 THEN 1 ELSE 0 END) AS "10<AGE<20",
SUM(CASE WHEN DATEDIFF(year, CREATEDATE, now()::date) >= 20 THEN 1 ELSE 0 END) AS "20<AGE"
FROM Materials
GROUP BY DEPARTMENT
You can use extract(year FROM age(createdate)) to get the exact age
i.e
select extract(year FROM age(timestamp '01-01-1989')) age
will give you
Result:
age
---
27
so you can use following select statement to get your desired output:
SELECT dept
,sum(CASE WHEN age < 10THEN 1 END) "age<10"
,sum(CASE WHEN age >= 10 AND age < 20 THEN 1 END) "10<age<20"
,sum(CASE WHEN age >= 20 THEN 1 END) "20<age"
FROM (
SELECT dept,extract(year FROM age(crdate)) age
FROM dt
) t
GROUP BY dept
If you don't want to use a sub select use this.
SELECT dept
,sum(CASE WHEN extract(year FROM age(crdate)) < 10THEN 1 END) "age<10"
,sum(CASE WHEN extract(year FROM age(crdate)) >= 10 AND extract(year FROM age(crdate)) < 20 THEN 1 END) "10<age<20"
,sum(CASE WHEN extract(year FROM age(crdate)) >= 20 THEN 1 END) "20<age"
FROM dt
GROUP BY dept

SQL server full join query

I have a full join sql query and i am retrieving the data from the same table.the problem is i am getting the null value where i am expecting the column name.
Example:
I am having a table where there are two columns typeOfPost,dob.
DOB TypeOfPost
--------- --------------
20/11/1998 Manager
1/1/2000 Sales
13/6/1999 Manager
20/1/1987 Manager
1/11/1985 Sales
Now when I am writing a join query like
select DATENAME(month,dob) as Red,count(TypeOfPost)
from tablename
where TypeOfPost='Manager'
group by DATENAME(month,dob) as A
full join
select DATENAME(month,dob) as Green,count(TypeOfPost)
from tablename
where TypeOfPost='Sales'
group by DATENAME(month,dob) as B on B.Green = A.Red
Output-- Expected Output--
--------------------- ---------------------
Month Man Sal Month Man Sal
-------- ----- ------ -------- ----- ------
January 1 1 January 1 1
NULL 1 NULL June 1 NULL
November 1 1 November 1 1
Now here the problem rise, I want 'June' in the column Month instead of NULL value.
So is there any way to get that??
Help me out.
Thanks.
One option is to
use a CASE statement in a subselect
Determine for given record if it is a manager or sales
substitute with 1 or 0 accordingly
SELECT and GROUP from this subselect the final results.
SQL Statement
SELECT Month
, SUM(Man) AS Man
, SUM(Sal) AS Sal
FROM (
SELECT DATENAME(MONTH, DOB) AS Month
, CASE WHEN TypeOfPost = 'Manager' THEN 1 ELSE 0 END AS Man
, CASE WHEN TypeOfPost = 'Sales' THEN 1 ELSE 0 END AS Sal
FROM tableName
) g
GROUP BY
Month
or
SELECT Month
, SUM(Man)
, SUM(Sal)
FROM (
SELECT DATENAME(MONTH, DOB) AS Month
, COUNT(TypeOfPost) AS Man
, 0 AS Sal
FROM tableName
WHERE TypeOfPost = 'Manager'
GROUP BY
DATENAME(MONTH, DOB)
UNION ALL
SELECT DATENAME(MONTH, DOB) AS Month
, 0 AS Man
, COUNT(TypeOfPost) AS Sal
FROM tableName
WHERE TypeOfPost = 'Sales'
GROUP BY
DATENAME(MONTH, DOB)
) g
GROUP BY
Month