I have a DAX query which I'm running via SSRS to produce a yearly summarized result set, with multiple group by, filters, measures etc. but for the question I'll use a simplified version.
It looks like this:
EVALUATE
SUMMARIZECOLUMNS(
'Years'[Year],
'Currency'[CurrencyName],
"SalesAmount", [SalesAmount]
)
2017, EUR, 1000000
2018, EUR, 1000000
2019, EUR, 1000000
2020, EUR, 1000000
2017, USD, 500000
2018, USD, 500000
2019, HKD, 1000000
2020, HKD, 1000000
This all works fine so far.
However, I have a reporting requirement to total for each currency. However, the total should not include the current year (which I can pass in from a parameter if needed)
To replicate what the current SQL based report already does, Ideally I'd also like to add an order by sequence to the result set, in order to format the report. The end result set would look like this:
1, 2017, EUR, 1000000
2, 2018, EUR, 1000000
3, 2019, EUR, 1000000
4, Total, EUR, 4000000
5, 2020, EUR, 1000000
1, 2017, USD, 500000
2, 2018, USD, 500000
3, Total, USD, 1000000
1, 2019, HKD, 1000000
2, Total HKD, 1000000
3, 2020, HKD, 1000000
The SQL which does this is quite convoluted, so I imagine this might not be possible in DAX, but if anyone has any suggestions on examples like this which they can suggest I'd appreciate it,
Thanks in advance,
W
This would get you the total rows as a new calculated table:
Totals =
VAR CurrYear =
YEAR ( TODAY () )
RETURN
SUMMARIZECOLUMNS (
'Currency'[Currency],
"SalesAmount", CALCULATE ( [SalesAmount], 'Years'[Year] < CurrYear ),
"Index", CALCULATE ( COUNT ( 'Years'[Year] ), 'Years'[Year] < CurrYear ) + 1
)
I bet you can figure out how to union this with the table you've already to get what the combined table you want.
You can try something like this:
Cumulative =
VAR currYear =
SELECTEDVALUE ( 'Table'[Date].[Year] )
RETURN
SUMX (
FILTER ( ALLSELECTED ( 'Table' ), 'Table'[Date].[Year] < currYear ),
'Table'[Value]
)
Related
Re-do Times 2 for clarity
For simplicity and clarity, the data I am aggregating is in a database I will refer to as "BaseA".
Normally, when comparing Month over month data, I can use the following query:
select date_trunc('month',hour) as date,
sum(a) as total_a,
sum(b) as available_b,
sum(c) as c,
sum(d) as net_d
from BaseA where id=12345 and hour >='2022-01-01'
group by date order by date desc
Instead of looking back and collecting ALL months from 2021-2022 for the duration I wish to view, I want to collect ONLY two months of data, those being the following:
October 2021
vs.
April 2022
I'd like the data to be visualized in the Month over month format, like so:
example
However, I would like to:
Select all BaseA columns (aka, select *)
Only include Two rows: April 2022 & October 2021
So, should come out like so:
example 2
This query is what Im trying to do (in word form since I can't write it)
Select *
from BaseA
where date
is in
April 2022
&
October 2022
The result of the above should result in 2 rows of data (one for each month referenced)
Is there a place in the below query where DISTINCT would make that actualized?
select * from BaseA
where id=12345 and
(
(month(month) = '04' and year(month) = '2022')
OR
(month(month) = '10' and year(month) = '2021')
)
--DISTINCT go where?
Appreciate the help!
To get data for the two months try something like this:
select * from BaseA
where _id=12345 and
(
(month(month) = '04' and year(month) = '2022')
OR
(month(month) = '10' and year(month) = '2021')
)
I have no idea what you mean by "return data consolidated (grouped) by month (so multiple lines occur per month)". Can you provide dummy data that illustrates the data that you have and the result that you want to achieve?
I'm running a points system for companies where every employee that works for that company is worth some points.
Every month the points for the companies are calculated.
This works so far, however In the 9th month of this year I would like to give double points for each acquired employee in that month.
I don't know how to do that.
I have this query now:
SELECT company, (employees *2) as "Points"
FROM data
WHERE month = '10'
GROUP BY company
But as you can see I give 2 points for each employee that works for that company in that month.
But for month 9 I want to give double points and add them to current points in current month(10)
I have this SQLfiddle as example: http://sqlfiddle.com/#!9/2cb812/7
Expected result:
company Points
__________________
company 1 26 + (extra points from month 9)
company 2 32 + (extra points from month 9)
company 3 44 + (extra points from month 9)
So it's all about the August/September delta 2018. If you run the query for any month before September 2018 (June 2018, May 2012, whatever), you just want to get the current month's points. If you run the query for any month after August 2018 (December 2018, March 2022, ...) you want the 2018 bonus points added.
Group by company and use conditional aggregation (an aggregation function on a condition) in order to calculate this.
We must look at the requested month (e.g. 10/2018) and August 2018 and September 2018.
SET #yearmonth = '201810';
SELECT
company,
SUM(
CASE WHEN yearmonth = #yearmonth THEN employees * 2 ELSE 0 END +
CASE WHEN #yearmonth >= '201809' AND yearmonth = '201809' THEN employees * 4 ELSE 0 END -
CASE WHEN #yearmonth >= '201809' AND yearmonth = '201808' THEN employees * 4 ELSE 0 END
) AS points
FROM data
WHERE yearmonth in ('201808', '201809', #yearmonth)
GROUP BY company
ORDER BY company;
The WHERE clause is superfluous, as the months are checked inside the SUM function, but it may speed up the query.
Rextester demo: https://rextester.com/ELOWTL44361
Trying to search in a database a date range. Problem is, I cannot use the datetime column type in my database. To compensate, date's are displayed as three columns. a Month column, a Day column, and a Year column. Here is my SQL query:
SELECT COUNT(*)
FROM `import`
WHERE `call_day` BETWEEN 29 AND 15
AND `call_month` BETWEEN 9 AND 10
AND `call_year` BETWEEN 2013 AND 2013
You can see where I run into trouble. call_day needs to search between the 29th day and the 15th day. This won't work because 15 is smaller than 29, but I need it to work because the month is in the future :)
Any thoughts/solutions? No I cannot change the database in any way. Read only.
Concat the values like yyyymmdd and then you can compare them like strings.
Besides the concatenation approach, which can be implemented in quite a few ways, e.g.
SELECT *
FROM import
WHERE STR_TO_DATE(CONCAT_WS('-', call_year, call_month, call_day), '%Y-%c-%e')
BETWEEN '2013-09-29' AND '2013-10-15'
or
SELECT *
FROM import
WHERE CONCAT(call_year, LPAD(call_month, 2, '0'), LPAD(call_day, 2, '0'))
BETWEEN '20130929' AND '20131015'
Here is SQLFiddle demo
that will always cause a full scan and assuming that date ranges in your queries usually don't span more than a few months you can also do
SELECT *
FROM import
WHERE (call_year = 2013 AND
call_month = 9 AND
call_day BETWEEN 29 AND DAY(LAST_DAY('2013-09-01'))) -- or just 30
OR (call_year = 2013 AND
call_month = 10 AND
call_day BETWEEN 1 AND 15)
Here is SQLFiddle demo
For a query that spans a year (e.g. from 2012-08-20 to 2013-10-15)
SELECT *
FROM import
WHERE (call_year = 2012 AND
call_month = 8 AND
call_day BETWEEN 20 AND 31)
OR (call_year = 2012 AND
call_month BETWEEN 9 AND 12 AND
call_day BETWEEN 1 AND 31)
OR (call_year = 2013 AND
call_month BETWEEN 1 AND 9 AND
call_day BETWEEN 1 AND 31)
OR (call_year = 2013 AND
call_month = 10 AND
call_day BETWEEN 1 AND 15)
Here is SQLFiddle demo
As PeterM said, this may play hell with performance, but if you're storing these as integers, you can do the following:
(year * 10000) + (month* 100) + day will always yield an 8 byte "date".
2013 *10000 = 20130000
9 * 100 = 900
15
20130915
It's an ugly hack, and it will be expensive, because you'll be evaluating every row in your table, if you can't limit the rows in any other way, but I think it'll work.
EDIT:
Typing is hard!
SELECT COUNT(*) FROM `import`
WHERE CONCAT(CAST(`call_year`AS CHAR(4)) , RIGHT(CONCAT('00',CAST(`call_month`AS CHAR(2))),2) , RIGHT(CONCAT('00',CAST(`call_day`AS CHAR(2))),2))
BETWEEN '20130929' AND '20131015'
Assume I have a table that contains a from_month (int, 1-12) and a to_month (int, 1-12) column, and I then have another table that contains a from (int, unix timestamp) and to (int, unix timestamp) column.
How would I match rows where the from_month and to_month columns contain my from and to timestamps, taking into account year boundaries on either side?
Scenario:
With from_month as 11 and to_month as 2, is there a way where I can get a from value of 1383264000 and a to value of 1391212800 to match?
At the same time however:
With from_month as 3 and to_month 8, is there a way where I can get a from value of 1362096000 and a to value of 1375315200 to match?
Clarifications:
I need records from the second table where the months from both timestamps fall between the range set out from the first table.
Also, I can obtain the month from the timestamp, so it's not necessary for MySQL to parse it, but the solution is likely unaffected.
Sorry but i'm not sure how you want to compare with the year
But if only month, you can use FROM_UNIXTIME to get date from a unixtime, and MONTH to get month from a date
for example
SELECT FROM_UNIXTIME(1391212800)
> February, 01 2014 00:00:00+0000
SELECT MONTH(FROM_UNIXTIME(1391212800))
> 2
with the month get from unix time, I'm sure you can do the rest or help explain more detail what you want to.
Hope this help
Here is the SQL how to create unix timestamp from your input :
SELECT
#year:= YEAR(CURDATE()),
#from:= CONCAT(#year, '-', 11, '-1') AS from_date,
#to:= CONCAT(IF(11<=2, #year, #year+1), '-', 2, '-1') AS to_date,
UNIX_TIMESTAMP(#from) AS from_timestamp,
UNIX_TIMESTAMP(#to) AS to_timestamp;
Month 11 & 2 are hard-coded. I used variables, so the sql is shorter.
SQL Fiddle.
Your scenario is :
With from_month as 11 and to_month as 2, is there a way where I can get a from value of 1383264000 and a to value of 1391212800 to match?
Which is from 2013-11-01 00:00:00 to 2014-02-01 00:00:00. Are you sure you need TO date to be from first of the month? or the last day of the Februar?
A solution was found that does an inclusive month check using months as integers from 0-11.
# Filter
SET #Fs = 2; # Mar
SET #Fe = 7; # Aug
#
SET #Ps = 2; # Mar
SET #Pe = 4; # May
SELECT
(
(
MOD(#Pe - #Fs + 24, 12) <= MOD(#Fe - #Fs + 24, 12)
)
AND (
MOD(#Ps - #Fs + 24, 12) <= MOD(#Pe - #Fs + 24, 12)
)
)
OR MOD(#Fe - #Fs + 24, 12)=11;
Problem:
I have a database of sensor readings with a timestamp for the time the sensor was read. Basically it looks like this:
Sensor | Timestamp | Value
Now I want to make a graph out of this data and I want to make several different graphs. Say I want one for the last day, one for the last week and one for the last month. The resolution of each graph will be different so for the day-graph the resolution would be 1 minute. For the week graph it would be one hour and for the month graph it would be one day, or quarter of a day.
So I would like an output that is the average of each resolution (eg. Day = Average over the minute, Week = Average over the hour and so on)
Ex:
Sensor | Start | End | Average
How do I do this easily and quickly in mySQL? I suspect it invoves creating a temporary table or sorts and joining the sensor data with that to get the average values of the sensor? But my knowledge of mySQL is limited at best.
Is there a really clever way to do this?
SELECT DAY(Timestamp), HOUR(Timestamp), MINUTE(Timestamp), AVG(value)
FROM mytable
GROUP BY
DAY(Timestamp), HOUR(Timestamp), MINUTE(Timestamp) WITH ROLLUP
WITH ROLLUP clause here produces extra rows with averages for each HOUR and DAY, like this:
SELECT DAY(ts), HOUR(ts), MINUTE(ts), COUNT(*)
FROM (
SELECT CAST('2009-06-02 20:00:00' AS DATETIME) AS ts
UNION ALL
SELECT CAST('2009-06-02 20:30:00' AS DATETIME) AS ts
UNION ALL
SELECT CAST('2009-06-02 21:30:00' AS DATETIME) AS ts
UNION ALL
SELECT CAST('2009-06-03 21:30:00' AS DATETIME) AS ts
) q
GROUP BY
DAY(ts), HOUR(ts), MINUTE(ts) WITH ROLLUP
2, 20, 0, 1
2, 20, 30, 1
2, 20, NULL, 2
2, 21, 30, 1
2, 21, NULL, 1
2, NULL, NULL, 3
3, 21, 30, 1
3, 21, NULL, 1
3, NULL, NULL, 1
NULL, NULL, NULL, 4
2, 20, NULL, 2 here means that COUNT(*) is 2 for DAY = 2, HOUR = 20 and all minutes.
Not quite the result table you wanted, but here's a starter for doing a 1 minute resolution:
SELECT sensor,minute(timestamp),avg(value)
FROM table
WHERE <time period specifier limits to a single hour>
GROUP BY sensor, minute(timestamp)
I've used code very similar to this (untested, but it's taking from working code)
set the variables:
$seconds = 3600;
$start = mktime(...); // say 2 hrs ago
$end = .... // 1 hour after $start
then run the query
SELECT MAX(`when`) AS top_When, MIN(`when`) AS low_When,
ROUND(AVG(sensor)) AS Avg_S,
(MAX(`when`) - MIN(`when`)) AS dur, /* the duration in seconds of the actual period */
((floor(UNIX_TIMESTAMP(`when`) / $seconds)) * $seconds) as Epoch
FROM `sensor_stats`
WHERE `when` >= '$start' AND `when` <= '$end' and duration=30
GROUP BY Epoch/*((floor(UNIX_TIMESTAMP(`when`) / $seconds)) * $seconds)*/
The advantage of this is that you can have whatever time periods you want - and not even required to have them on 'round numbers', like a complete clock-hour (even a clock-minute, 0-59).