Conditional Time Period for Totals - sql-server

I feel like I'm making this harder than it should be. I'm trying to display a sum for month C but this sum must include totals for months A, B & C. Then I need to do the same thing for Month D which includes totals for months B, C & D. Once I have this figured out I need to break it down by individual accounts but that part shouldn't be too difficult.
I have a date table to call on but it doesn't have month start or end dates which seems to be causing my difficulty.

So the solution to the above issue is to use a CTE (Common Table Expression) in the join statement identifying the date range accepted for the time period.
Select *
FROM A
LEFT JOIN B CASE WHEN a.DateID >= b.PeriodStart AND a.DateID <= b.PeriodEnd THEN 1 ELSE 0 END = 1

Related

How can I add values to a chart that do not exist as 0 in google data studio?

I have got 4 tables in BigQuery that keep statistics for messages in a Message Queue. The tables are : receivedMessages, processedMessages, skippedMessages and failedMessages. Each table has among other things a header.processingMetadata.approximateArrivalTimestamp which as you might have guessed it is a timestamp field.
My purpose is to create 4 charts for each one of this tables aggregating in this field as well as a 5th chart that displays the percentage of each message category each day in regards to the receivedMessages as well as the unknown status messages using the following formula :
UNKNOWN_STATUS_MESSAGES = TOTAL_RECEIVED_MESSAGES - (TOTAL_PROCESSED_MESSAGES + TOTAL_SKIPPED_MESSAGES + TOTAL_FAILED_MESSAGES)
However some days do not have skipped or failed messages, therefore there are no records in Big Query in these two tables. This results to these 2 graphics having dates missing and also not displaying correctly the UNKNOWN_STATUS_MESSAGES in the 5th graph.
I also used the following code as a metric in my graphs with no success (changing the variable name appropriately each time).
CASE WHEN TOTAL_FAILED_MESSAGES IS NULL THEN 0 ELSE TOTAL_FAILED_MESSAGES END
Is there a way to make google data studio to fill the dates with no data with 0s so I can display the charts correctly?
As long as you know the date boundaries of your chart, you can fill those holes with zeros. For instance, if you want to generate your report for last 30 days:
with dates as (
select
x as date
from
unnest(generate_date_array(date_sub(current_date(), interval 30 day), current_date())) as x
)
select
date,
received_messages,
processed_messages,
skipped_messages,
failed_messages,
received_messages - (processed_messages + skipped_messages + failed_messages) as unknown_messages from (
select
d.date,
coalesce(count(received.*), 0) as received_messages,
coalesce(count(processed.*), 0) as processed_messages,
coalesce(count(skipped.*), 0) as skipped_messages,
coalesce(count(failed.*), 0) as failed_messages
from dates d
left join dataset.receivedMessages received
on date(received.header.processingMetadata.approximateArrivalTimestamp) = d.date
left join dataset.processedMessages processed
on date(processed.header.processingMetadata.approximateArrivalTimestamp) = d.date
left join dataset.skippedMessages skipped
on date(skipped.header.processingMetadata.approximateArrivalTimestamp) = d.date
left join dataset.failedMessages failed
on date(failed.header.processingMetadata.approximateArrivalTimestamp) = d.date
group by 1
)
order by 1
1) I recommend doing a join in BigQuery with a date master table to return '0' for those date values.
2) Otherwise, in Data Studio, make sure there is a field X that has values for all dates. Then create a calculated field with formula X - X + TOTAL_SKIPPED_MESSAGES and X - X + TOTAL_FAILED_MESSAGES
As I found out it is also possible to do it in non fixed date using date parameters. So the first part of khan's answer can be rewritten as:
WITH dates AS (
select *
from unnest(generate_date_array(PARSE_DATE('%Y%m%d', #DS_START_DATE), PARSE_DATE('%Y%m%d', #DS_END_DATE), interval 1 day)) as day
)

How do I nest if then statements with select statements including other if then statements?

I'm fairly new to SQL and can't figure out how to combine several if .. then statements.
What is the right syntax for this?
I'm using SQL Server Management Studio 2017.
I've tried to combine if... else if..statements and I tried using case statements, but I always get lost in the nesting of the statements.
I have several condtions whom have to be met before I can execute some sort of calculation.
It should be something like this:
If CalculationMethod = x
and if (Price * coefficient) < Amount
then CalculatedAmount = Amount
else CalculatedAmount = (Price * coefficient)
Where Amount has it's own if statements:
Amount =
If Category = a and DistanceFrom <= Distance >= DistanceUntill then take amount from that particular cell
If Category = b and DistanceFrom <= Distance >= DistanceUntill then take amount from that particular cell
If Category = c and DistanceFrom <= Distance >= DistanceUntill then take amount from that particular cell
In this case, Amount is a cell in a table with columns DistanceFrom, DistanceUntill, a, b and c.
CalculationMethod and Coefficient are columns in another table.
Price is a column in third table.
In the end I want the CalculatedAmount based on the Amount, Price and Coefficient.
Does this make any sense? Does anyone has an idea on how to tackle this?
If you have an IF...THEN...ELSE type of scenario I think the right direction would be to use a CASE statement such as:
SELECT CASE WHEN CalculationMethod = x AND ((Price * coefficient) < Amount) THEN Amount
ELSE (Price * coefficient) END CalculatedAmount
You can read about it here: https://learn.microsoft.com/en-us/sql/t-sql/language-elements/case-transact-sql?view=sql-server-2017
An IIF clause works very well when there is only one decision branch, but if you have multiple things to choose from the CASE statement is the way to go.
SELECT IIF(CalculationMethod = x and Price * coefficient < Amount, Amount, Price * coefficient) as CalculatedAmount
FROM aTable

How to get row limiting by value from two columns

Please advice how to combine query, to find first occurrence of row
I have table
Date Counter
01.01.2005 208,5
02.01.2005 209,5 <----- start
03.01.2005 210,5
04.01.2005 211,5
08.01.2005 16,5
09.01.2005 17,2
10.01.2005 18,8 <------ correct
11.01.2005 19,7
12.01.2005 20,7
13.01.2005 21
14.01.2005 116,3
15.01.2005 120,4
16.01.2005 135,2
17.01.2005 1,1
18.01.2005 10,3
19.01.2005 18,7 <------ wrong
20.01.2005 14,2
21.01.2005 8,5
22.01.2005 7,1
and I need to extract Date by Counter 18.5 (from starting date 02.01.2005), since this value not in the table possible to take next higher value.
I tried to search by using starting date (because table have thousand dates and Counter between 0-499) and limit for value (grater or equal).
select top 1 Date from Tabel1 where Date > 02.01.2005 AND Counter >= 18.5
this query return wrong result - date 03.01.2005,
but correct must be 10.01.2005.
Hope for any assistance. Thanks in advance.
(I use: sql 2008 r).
You need to put an ORDER BY clause when using TOP. In this case you want to get the lowest Counter:
SELECT TOP 1 *
FROM tbl
WHERE
Date > CAST('20050102' AS DATE)
AND Counter >= 18.5
ORDER BY Counter
Note that without the ORDER BY, the result of TOP 1 is not guaranteed to always be the same.

Report Builder 3.0 - grouping rows by time of day

I am trying to create a table within a report that appears as follows:
The data set is based on this query:
SELECT
DATENAME(dw, CurrentReadTime) AS 'DAY',
DATEPART(dw, CurrentReadTime) AS 'DOW',
CAST(datename(HH, CurrentReadTime) as int) AS 'HOD',
AVG([Difference]) AS 'AVG'
FROM
Consumption
INNER JOIN Readings ON Readings.[RadioID-Hex] = Consumption.[RadioID-Hex]
WHERE
CONCAT([Building], ' ', [Apt]) = #ServiceLocation
GROUP BY
CurrentReadTime
ORDER BY
DATEPART(DW, CurrentReadTime),
CAST(DATENAME(HH, CurrentReadTime) AS INT)
The data from this table returns as follows:
In report builder, I have added this code to the report properties:
Function GetRangeValueByHour(ByVal Hour As Integer) As String
Select Case Hour
Case 6 To 12
GetRangeValueByHour = "Morning"
Case 12 to 17
GetRangeValueByHour = "Afternoon"
Case 17 to 22
GetRangeValueByHour = "Evening"
Case Else
GetRangeValueByHour = "Overnight"
End Select
Return GetRangeValueByHour
End Function
And this code to the "row group":
=Code.GetRangeValueByHour(Fields!HOD.Value)
When I execute the report, selecting the parameter for the target service location, I get this result:
As you will notice, the "Time of Day" is displaying the first result that meets the CASE expression in the Report Properties code; however, I confirmed that ALL "HOD" (stored as an integer) are being grouped together by doing a SUM on this result.
Furthermore, the actual table values (.05, .08, etc) are only returning the results for the HOD that first meets the requirements of the CASE statement in the VB code.
These are the things I need resolved, but can't figure out:
Why isn't the Report Properties VB code displaying "Morning", "Afternoon", "Evening", and "Overnight" in the Time of Day column?
How do I group together the values in the table? So that the AVG would actually be the sum of each AVG for all hours within the designated range and day of week (6-12, 12-18, etc on Monday, Tuesday etc).
To those still reading, thanks for your assistance! Please let me know if you need additional information.
I'm still not sure if I have a clear picture of your table design, but I'm imagining this as a single row group that's grouped on this expression: =Code.GetRangeValueByHour(Fields!HOD.Value). Based on this design and the dataset above, here's how I would solve your two questions:
Use the grouping expression for the value of the Time of Day cell, like:
Add a SUM with a conditional for the values on each day of the week. Example: the expression for Sunday would be =SUM(IIF(Fields!DOW.Value = 1, Fields!AVG.Value, CDec(0))). This uses CDec(0)instead of 0 because the AVG values are decimals and SSRS will otherwise throw an aggregate of mixed data types error by interpreting 0 as an int.

SQL Server 2008 - datediff month

I have the following query below --
SELECT *
FROM
dbo.patient AS p
INNER JOIN
dbo.study AS s ON s.patient_fk = p.pk
/***where p.pk = s.patient_fk***/
WHERE
s.study_custom1 LIKE'%hosp%'
AND s.study_datetime >= DATEADD(month, datediff(month, 0, getdate()) -1, 0)
AND s.study_datetime < DATEADD(month, datediff(month, -1, getdate()) -1, 1)
I want to find all dates for the most recent prior month. In this case it is the month of July, but the query is picking up '8/1/2014' along with July dates.
How do I need to modify this to exclude August and only find July?
Thanks
Try this
select *
from dbo.patient AS p
INNER JOIN dbo.study AS s
on s.patient_fk = p.pk
/***where p.pk = s.patient_fk***/
where s.study_custom1 like '%hosp%'
and DATEPART(year,s.study_datetime) = DATEPART(year,DATEADD(month,-1,GETDATE()))
and DATEPART(month,s.study_datetime) = DATEPART(month,DATEADD(month,-1,GETDATE()))
EDIT: Note: You could speed execution by setting variables instead of computing the month and year of today for every row, but as you had your original in a query without variables I provided a less computationally efficient solution for the sake of making sure you could use it in your application.
Also note that the arithmetic for getting the year and month of "last month" must be done inside the DATEPART so the query will function correctly in January.
EDIT 2: Explaining optimization
My first edit was written before I saw your comment, so I'm not sure if you're asking for an explanation of my original query, or asking for an explanation of my suggested optimization, but I'll guess the optimization, I'll be happy to further explain the query if that's what you meant.
So the weakness of my first solution is it makes 8 function calls per row. Not a big deal, but it could add up for a large database. If you instead define variables that you populate outside the query, you can reduce it to a pair of index scans per row, which is significantly faster. Significantly being maybe 1/10 of a second if you only have thousands of patients :-)
But regardless, here is a more efficient solution.
DECLARE #StartM DATE
DECLARE #EndM DATE
SET #StartM = DATEADD(month,-1,CONVERT(DATE,SUBSTRING(CONVERT(VARCHAR(50),GETDATE(),20),1,8)+'01'))
SET #EndM = DATEADD(month,1,#StartM)
select *
from dbo.patient AS p
INNER JOIN dbo.study AS s
on s.patient_fk = p.pk
/***where p.pk = s.patient_fk***/
where s.study_custom1 like '%hosp%'
and s.study_datetime >=#StartM
and s.study_datetime < #EndM
Note that #EndM will be the midnight of the first day of this month, so even if your s.study_datetime is 11:59 PM on the last day of last month it still gets included. If you build an index on s.study_datetime, this will be very fast.

Resources