filter by Sum without Grouping - sql

i have a resultset that i generate from a query that Looks like this:
Select Employee, Month, (select case when Status = '---' then 0 Else 1 end) as PlaningValue
From PlanningTable PT
Where Month >= #From Month and Month <= #ToMonth
The Result of this Looks something like this:
|Employee| Month | PlaningValue |
|George | 2014-01 | 1 |
|George | 2014-02 | 1 |
|George | 2014-03 | 0 |
|Andrew | 2014-01 | 0 |
|Andrew | 2014-02 | 1 |
|Andrew | 2014-03 | 0 |
|Howard | 2014-01 | 1 |
|Howard | 2014-02 | 1 |
|Howard | 2014-03 | 1 |
Now what i want is the following:
Filter out Employee's who, over the three month period, have a total planing Value of 3,
in the example above, Howard would be filtered out.
Is there a way to do this nicely or is it all just impossible to even thin ?
(Remark: Since i am going to use the Query on Reporting Services, i can't use the OVER function)
Thank you all for your help

This looks to be SQL Server syntax, as such I you can use windowed functions:
WITH CTE AS
( SELECT Employee,
Month,
PlanningValue = CASE WHEN Status = '---' THEN 0 ELSE 1 END,
Total = SUM(CASE WHEN Status = '---' THEN 0 ELSE 1 END)
OVER (PARTITION BY Employee)
FROM PlanningTable
WHERE Month >= #FromDate
AND Month <= #ToMonth
)
SELECT Employee, Month, PlanningValue
FROM CTE
WHERE Total != 3;
Simplified Example on SQL Fiddle

Try:
select pt.employee, pt.month, pt.planningvalue
from planningtable pt
join planningtable pt2 on pt.employee = pt2.employee
join planningtable pt3 on pt.employee = pt3.employee
join planningtable pt4 on pt.employee = pt4.employee
where month >= #mofrom and month <= #tomonth
and pt2.month = #tomonth
and pt3.month in (select month from planningtable where month > #mofrom and month < #tomonth)
and pt4.month = #mofrom
and pt2.planningvalue + pt3.planningvalue + pt4.planningvalue <> 3

Related

Do I need a CASE expression?

I have the following query which returns an organizations prior year (from the current year, so 2018) total wages.
SELECT
organization_id,
CASE
WHEN organization_id IN (SELECT text_1
FROM combo_table_detail
WHERE combo_table_id = 'wageAdjustment')
THEN SUM(ISNULL(component_value, 0)) + ISNULL(ctd.number_2, 0)
ELSE SUM(ISNULL(component_value, 0))
END AS "total_annual_wage",
MAX((begin_date)) AS "total_annual_wage_eff_date"
FROM
actual_pay_hours aph
LEFT JOIN
combo_table_detail ctd ON aph.organization_id = ctd.text_1
AND combo_table_id = 'wageAdjustment'
WHERE
organization_id = 'Org1'
AND component_name IN ('earnDef', 'earnings')
AND begin_date >= DATEADD(YEAR, DATEDIFF(YEAR, 0, GETDATE()) - 1, 0)
AND begin_date < DATEADD(YEAR, DATEDIFF(YEAR, 0, GETDATE()), 0)
GROUP BY
organization_id, ctd.number_2
However, I've run across an issue where some organizations either don't have a prior year (some only have 2019 wages), or their latest wages are from 2014. In both cases, the query returns blank values. This is due to the line
AND begin_date >= DATEADD(YEAR, DATEDIFF(YEAR, 0, GETDATE()) - 1, 0)
AND begin_date < DATEADD(YEAR, DATEDIFF(YEAR, 0, GETDATE()), 0)
The expected result should look something like this:
+-----------------+-------------------+----------------------------+
| organization_id | total_annual_wage | total_annual_wage_eff_date |
+-----------------+-------------------+----------------------------+
| Org1 | 50000 | 12/1/2018 |
+-----------------+-------------------+----------------------------+
But instead, it looks like this:
+-----------------+-------------------+----------------------------+
| organization_id | total_annual_wage | total_annual_wage_eff_date |
+-----------------+-------------------+----------------------------+
This issue seems to be the fact that in the aph table, some units have wages for the 2018 year, while others don't. Example:
SELECT DISTINCT
YEAR(BEGIN_DATE) AS [Begin Date for Org1]
FROM
ACTUAL_PAY_HOURS aph
WHERE
ORGANIZATION_ID = 'Org1'
Results:
+---------------------+
| Begin Date for Org1 |
+---------------------+
| 1988 |
| 1989 |
| 1990 |
| 1991 |
| 1992 |
| 1993 |
| 1994 |
| 2004 |
| 2005 |
| 2006 |
| 2007 |
| 2008 |
| 2009 |
| 2010 |
| 2011 |
| 2012 |
| 2013 |
| 2014 |
+---------------------+
Additionally, the total wages for the prior year are then being adjusted by a value in the CTD (combo_table_detail) table. The issue is that when there are no values for an exisitng year, nothing is returned. What I need is for the wage total to then be 0 - since there isn't any data for that year, but then the value from the CTD table is added.
So, if Org1 has no wages for 2018, it should come out like this:
+-----------------+-------------+-------------------+-------+
| organization_id | total_wages | combo_table_value | total |
+-----------------+-------------+-------------------+-------+
| Org1 | 0 | 25000 | 25000 |
+-----------------+-------------+-------------------+-------+
So my question is, what logic can I add to this query that will return a result when the Organization doesn't have any prior year wages, but will still be added to the CTD table resulting in a value being returned?
Assuming that 2 of the fields in the WHERE clause belong to the joined table.
Try with moving those criteria to the LEFT JOIN.
SELECT aph.organization_id,
ISNULL(SUM(ctd.component_value),0) +
(CASE
WHEN SUM(ctd.component_value) IS NULL
THEN SUM (
SELECT d.component_value
FROM combo_table_detail d
WHERE d.combo_table_id = 'wageAdjustment'
AND d.text_1 = aph.organization_id
)
ELSE 0
END) AS [total_annual_wage],
MAX(aph.begin_date) AS [total_annual_wage_eff_date]
FROM actual_pay_hours AS aph
LEFT JOIN combo_table_detail AS ctd
ON ctd.text_1 = aph.organization_id
AND ctd.combo_table_id = 'wageAdjustment'
AND aph.component_name IN ('earnDef', 'earnings')
AND aph.begin_date >= DATEFROMPARTS(YEAR(GETDATE())-1,1,1)
AND aph.begin_date < DATEFROMPARTS(YEAR(GETDATE()),1,1)
WHERE aph.organization_id = 'orgID'
GROUP BY aph.organization_id, ctd.number_2
SELECT
aph.organization_id,
MAX(aph.begin_date) AS total_annual_wage_eff_date,
--earn components sum
ISNULL( SUM(aph.component_value), 0)
+
--adjustment sum, if any
(
SELECT ISNULL(SUM(ctd.number_2), 0)
FROM combo_table_detail ctd
WHERE ctd.text_1 = aph.organization_id
AND ctd.combo_table_id = 'wageAdjustment'
) AS total_annual_wage
FROM actual_pay_hours AS aph
WHERE aph.component_name IN ( 'earnDef', 'earnings' )
AND aph.begin_date >= DATEFROMPARTS(YEAR(GETDATE())-1, 1, 1)
AND aph.begin_date < DATEFROMPARTS(YEAR(GETDATE()), 1, 1)
GROUP BY aph.organization_id

Summing By Count

I'm trying to create a Summation based on the Count number for a particular column. If you looks at the last line in the Select below you'll see that I tried implementing a CASE statement. However, it produces all NULL values. Which I believe I understand why (each row has a unique set of values) but I'm not sure how to fix my problem.
SELECT
TotalFilesProduced.ReviewDate,
TotalFilesProduced.FileReviewedByUserID,
TotalFilesProduced.FileSource,
TotalFilesProduced.FilesIndexed TotalIndexed,
TotalFilesProduced.FileNumberofPages TotalFileNumberofPages,
TotalFilesProduced.FilesProduced,
CASE WHEN COUNT(DISTINCT FileReviewedByUserID) > 1 THEN SUM(TotalFilesProduced.FilesIndexed) END
FROM
(SELECT
CAST(ibfp.FileReviewedDate AS DATE) ReviewDate,
ibfp.FileReviewedByUserID,
FileSource,
COUNT(*) FilesProduced,
COUNT(DISTINCT ibf.InboundFileID) FilesIndexed,
SUM(CASE WHEN ibfp.FromPage = ibfp.ToPage THEN 1
ELSE ibfp.ToPage-ibfp.FromPage + 1 END) [FileNumberofPages]
FROM
dbo.InboundFilePartitions ibfp
INNER JOIN dbo.InboundFiles ibf ON ibfp.InboundFileID = ibf.InboundFileID
WHERE
CAST(ibfp.FileReviewedDate AS DATE) >= '10/22/2014'
and CAST(ibfp.FileReviewedDate AS DATE) <= '10/22/2014'
and ibf.ProjectID in (110)
GROUP BY
CAST(ibfp.FileReviewedDate AS DATE),
ibfp.FileReviewedByUserID,
FileSource
) TotalFilesProduced
GROUP BY
TotalFilesProduced.ReviewDate,
TotalFilesProduced.FileReviewedByUserID,
TotalFilesProduced.FileSource,
TotalFilesProduced.FilesIndexed,
TotalFilesProduced.FileNumberofPages,
TotalFilesProduced.FilesProduced
Here is an example for further clarification - here the UserID 1036 producing a NULL is fine since it appear only once but for 804 - I would like to sum the TotalIndexed column so the NULL area should read 139 (for both instances that 804 appears)
ReviewDate | FilereviewedByUserID | FileSource | TotalIndexed | TotalFileNumberofPages | FilesProduced | (No Column Name) /*My Sum*/
------------------------------------------------------------------------------------------------------------------------------------
2014-10-22 | 804 | 1 | 1 | 67 | 1 | NULL
------------------------------------------------------------------------------------------------------------------------------------
2014-10-22 | 1036 | 1 | 1 | 17 | 1 | NULL
------------------------------------------------------------------------------------------------------------------------------------
2014-10-22 | 804 | 2 | 138 | 3322 | 184 | NULL
As stated in the comment
This will always be false
CASE WHEN COUNT(DISTINCT FileReviewedByUserID) > 1
Because of
GROUP BY ibfp.FileReviewedByUserID
And you have some other strange stuff
CAST(ibfp.FileReviewedDate AS DATE) >= '10/22/2014'
and CAST(ibfp.FileReviewedDate AS DATE) <= '10/22/2014'
is the same as
CAST(ibfp.FileReviewedDate AS DATE) = '10/22/2014'
More strange stuff
SUM(CASE WHEN ibfp.FromPage = ibfp.ToPage THEN 1
ELSE ibfp.ToPage-ibfp.FromPage + 1 END) [FileNumberofPages]
is the same as
SUM(ibfp.ToPage-ibfp.FromPage + 1) [FileNumberofPages]
not sure what you are trying to do but a group by on a group by is not common

Get Month columns from datetime column and count entries

I have the following table:
| ID | Name | DateA | TimeToWork | TimeWorked |
|:--:|:----:|:----------:|:----------:|:----------:|
| 1 |Frank | 2013-01-01 | 8 | 5 |
| 2 |Frank | 2013-01-02 | 8 | NULL |
| 3 |Frank | 2013-01-03 | 8 | 7 |
| 4 |Jules | 2013-01-01 | 4 | 9 |
| 5 |Jules | 2013-01-02 | 4 | NULL |
| 6 |Jules | 2013-01-03 | 4 | 3 |
The table is very long, every person has an entry for every day in a year. For each person I have the Date he worked (DateA), the hours he has to work according to contract (TimeToWork) and the hours he worked (TimeWorked). As you can see some days a person didnt work on a day he had to. This is when a person took a full day overtime.
What I try to accomplish is to get the following table out of the first one above.
| Name | January | Feburary | March | ... | Sum |
|:----:|:----------:|:--------:|:-----:|:---:|:---:|
|Frank | 2 | 0 | 1 | ... | 12 |
|Jules | 5 | 1 | 3 | ... | 10 |
For each month I want to count all days where a person took A FULL day off and sum all up in the Sum column.
I tried something like Select (case when Datetime(month, DateA = 1 then count(case when timetowork - (case when timeworked then 0 end) = timetowork then 1 else 0 end) end) as 'January' but my TSQL is just not that good and the code doent work at all. Btw using this my select command would be about 40 lines.
I really would appreciate if anyone could help me or give me a link to a good source so I can read myself into it.
If I understand the question right, than Gordon Linoff's answer is a good beginning, but doesn't deal with "full day off".
select Name,
sum(case when month(DateA) = 01 and TimeWorked is null then 1 else 0 end) as Jan,
sum(case when month(DateA) = 02 and TimeWorked is null then 1 else 0 end) as Feb,
...
sum(case when month(DeteA) = 12 and TimeWorked is null then 1 else 0 end) as Dec,
sum(case when TimeWorked is null then 1 else 0 end) as Sum
from table T
where year(DateA) = 2013
group by name
This method solves the problem?
The correct syntax is conditional aggregation:
select name,
sum(case when month(datea) = 1 then timeworked else 0 end) as Jan,
sum(case when month(datea) = 2 then timeworked else 0 end) as Feb,
. . .
sum(case when month(datea) = 12 then timeworked else 0 end) as Dec,
sum(timeworked)
from table t
where year(datea) = 2013
group by name;
The CASE can be removed using bit logic
SELECT name
, January = SUM((1 - CAST(MONTH(DateA) - 1 as bit))
* (1 - CAST(COALESCE(TimeWorked, 0) as bit)))
, February = SUM((1 - CAST(MONTH(DateA) - 2 as bit))
* (1 - CAST(COALESCE(TimeWorked, 0) as bit)))
...
, December = SUM((1 - CAST(MONTH(DateA) - 12 as bit))
* (1 - CAST(COALESCE(TimeWorked, 0) as bit)))
, Total = SUM((1 - CAST(COALESCE(TimeWorked, 0) as bit)))
FROM table1
GROUP BY name;
To check if there is a dayoff the formula is:
(1 - CAST(COALESCE(TimeWorked, 0) as bit))
that is equivalent to TimeWorked IS NULL: the CAST to BIT return 1 for every value different from 0, 1 - BIT invert those values.
The month filter is:
(1 - CAST(MONTH(DateA) - %month% as bit))
using the same idea as before this formula return 1 only for the given month (the cast give 1 for every other month, the 1 - BIT invert that result)
Multipling the two formulas we have the days off only for the given month
You can get your required result by using pivot also. You can get more information about pivot here http://technet.microsoft.com/en-in/library/ms177410(v=sql.105).aspx
Also you can get your output using the following query. I did it for up to April only. You can extend it up to December.
Select [Name], [January], [February], [March], [April]
From
(
Select Name, MName, DaysOff from
(
select Name, DATENAME(MM, dateA) MName,
count(case isnull(timeworked,0) when 0 then 1 else null end) DaysOff
from tblPivot
Where Year(DateA) = 2013
group by Name, DATENAME(MM, dateA)
) A ) As B
pivot(Count(DaysOff)
For MName in ([January], [February],[March],[April])
) As Pivottable;

How do you select from a date range as the data source

Short of creating a table with all of the values of a date range, how would I select from a datarange as a datasource.
What I'm trying to accomplish is to create a running total of all items created within the same week from separate tables, while showing weeks with 0 new
example table:
items
-----------------------------
created_on | name | type
-----------------------------
2012-01-01 | Cards | 1
2012-01-09 | Red Pen | 2
2012-01-31 | Pencil | 2
2012-02-01 | Blue Pen | 2
types
--------------
name | id
--------------
Fun | 1
Writing | 2
sample output:
----------------------------
year | week | fun | writing
----------------------------
2012 | 1 | 1 | 0
2012 | 2 | 0 | 1
2012 | 3 | 0 | 0
2012 | 4 | 0 | 0
2012 | 5 | 0 | 2
You could generate a number series for the week numbers
SELECT
w.week
FROM
(SELECT generate_series(1,52) as week) as w
Example
SELECT
w.year,
w.week,
COUNT(i1) as fun,
COUNT(i2) as writing
FROM (SELECT 2012 as year, generate_series(1,6) as week) as w
LEFT JOIN items i1 ON i1.type = 1 AND w.week = EXTRACT(WEEK FROM i1.created_on)
LEFT JOIN items i2 ON i2.type = 2 AND w.week = EXTRACT(WEEK FROM i2.created_on)
GROUP BY
w.year,
w.week
ORDER BY
w.year,
w.week
Very close erikxiv, but you got me in the right direction. I have multiple tables I need to grab information from, this the additional select in the select fields.
select
date_year.num,
date_week.num,
( select count(*) from items x
and EXTRACT(YEAR FROM x.created_on) = date_year.num
and EXTRACT(WEEK FROM x.created_on) = date_week.num
) as item_count
from
(SELECT generate_series(2011, date_part('year', CURRENT_DATE)::INTEGER) as num) as date_year,
(SELECT generate_series(1,52) as num) as date_week
where
(
date_year.num < EXTRACT (YEAR FROM CURRENT_DATE)
OR
(
date_year.num = EXTRACT (YEAR FROM CURRENT_DATE) AND
date_week.num <= EXTRACT (WEEK FROM CURRENT_DATE)
)
)

Putting stuff into date ranges in SQL Server 2005

I have a table with week ranges (week number,start date, end date) and a table with tutorial dates (for writing tutors (tutor ID, tutorial_date, tutorial type(A or B).
I want to create two query that shows the week ranges (week 1, week 2) across the top with the tutor names on the side with count of tutorials (of type "A") in that week's date range in each block for that week.
The result should look like this:
Counts of Tutorials of Type "A"
Tutor|Week One|Week Two|Week Three|Week Four|Total
Joe | 3 | 5 | 7 | 8 | 23
Sam | 2 | 4 | 3 | 8 | 17
Meaning that Joe completed 3 tutorials in week one, five in week two, 7 in week three, and 8 in week 4.
The second query should show totals for tutorial type "A" and type "B"
Tutor|Week One|Week Two|Week Three|Week Four|Total |
Joe | 3/1 | 5/3 | 7/2 | 8/2 | 23/8 |
Sam | 2/3 | 4/4 | 3/2 | 8/3 | 17/12 |
Here, in Week One, Joe has done 3 tutorials of type A and 1 of type B.
Sample table data for tutorials (week one)
Tutor | Tutorial_ID | Tutorial Date |Type|
------------------------------------------
Joe | 1 | 2011-01-01 | A |
Joe | 2 | 2011-01-02 | A |
Joe | 3 | 2011-01-03 | A |
Joe | 4 | 2011-01-03 | B |
Sam | 5 | 2011-01-01 | A |
Sam | 6 | 2011-01-02 | A |
Sam | 7 | 2011-01-03 | B |
The week table looks like this:
weekNumber |startDate |endDate
1 |2011-01-01|2011-01-15
I'd like to gen this in SQL Server 2005
There are a few ways to do this.
For query one, where you only need to PIVOT on type 'A' then you can do just a PIVOT
select *
from
(
select w1.tutor
, w1.type
, wk.weeknumber
from w1
inner join wk
on w1.tutorialdate between wk.startdate and wk.enddate
where w1.type = 'a'
) x
pivot
(
count(type)
for weeknumber in ([1])
)p
See SQL Fiddle with Demo
Or you can use a Count() with a CASE statement.
select w1.tutor
, COUNT(CASE WHEN w1.type = 'A' THEN 1 ELSE null END) [Week One]
from w1
inner join wk
on w1.tutorialdate between wk.startdate and wk.enddate
group by w1.tutor
See SQL Fiddle with Demo
But for the second query, I would just use a Count() with a CASE
select w1.tutor
, Cast(COUNT(CASE WHEN w1.type = 'A' AND wk.weeknumber = 1 THEN 1 ELSE null END) as varchar(10))
+ ' / '
+ Cast(COUNT(CASE WHEN w1.type = 'B' AND wk.weeknumber = 1 THEN 1 ELSE null END) as varchar(10)) [Week One]
, Cast(COUNT(CASE WHEN w1.type = 'A' AND wk.weeknumber = 2 THEN 1 ELSE null END) as varchar(10))
+ ' / '
+ Cast(COUNT(CASE WHEN w1.type = 'B' AND wk.weeknumber = 2 THEN 1 ELSE null END) as varchar(10)) [Week Two]
from w1
inner join wk
on w1.tutorialdate between wk.startdate and wk.enddate
group by w1.tutor
See SQL Fiddle with Demo
Edit as AndriyM pointed out the second could be done with a PIVOT here is a solution for the Second query:
SELECT *
FROM
(
select distinct w1.tutor
, wk.weeknumber
, left(total, len(total)-1) Totals
FROM w1
inner join wk
on w1.tutorialdate between wk.startdate and wk.enddate
CROSS APPLY
(
SELECT cast(count(w2.type) as varchar(max)) + ' / '
from w1 w2
inner join wk wk2
on w2.tutorialdate between wk2.startdate and wk2.enddate
WHERE w2.tutor = w1.tutor
AND wk2.weeknumber = wk.weeknumber
group by w2.tutor, wk2.weeknumber, w2.type
FOR XML PATH('')
) D ( total )
) x
PIVOT
(
min(totals)
for weeknumber in ([1], [2])
) p
See SQL Fiddle with Demo