mysql how to combine two select statement - mysql

I have a database with records of date-time and a measurement value.
I've been writing two separate queries, one to return the total count of all daily records between certain times of day for the previous month, and the same query but a count of only when the measurement value is below threshold. I then manually divide the theshold count by total count for each day, and I am able to get a % uptime or SLA.
So I have two questions:
1) Can I combine these two queries into one query. I found the Answer to #1, see below
2) Can I go ahead and do the math in the queries, so what I get returned is just a listing of each day, the count above, the count below, and the % above or below threshold...
Sample data and query are listed below.
TableA
hostname, date_time, value
Sample Query to return days from previous month, excluding weekend days.
SELECT
count(*),
DATE(date_time),
SUM(
CASE WHEN rssi_val < 100
THEN 1
ELSE 0
END
)
FROM TableA
WHERE hostname = 'hostA'
AND DATE(date_time) BETWEEN '2013-07-01' AND '2013-07-31'
AND TIME(date_time) BETWEEN '06:00:00' AND '18:00:00'
AND DAYOFWEEK(date_time) NOT IN (1, 7)
GROUP BY DATE(date_time);
So now I just want to know how to add a 4th column that gives the percent uptime/downtime.

Have you tried this ?
select count(*),
DATE(date_time),
SUM(CASE WHEN rssi_val<100 THEN 1 ELSE 0 END),
SUM(CASE WHEN rssi_val<100 THEN 1 ELSE 0 END)/count(*) as percentage
from TableA
where hostname='hostA'
and DATE(date_time) between '2013-07-01' and '2013-07-31'
and TIME(date_time) between '06:00:00' and '18:00:00'
and DAYOFWEEK(date_time) NOT IN (1,7)
group by DATE(date_time);

Related

How to query rows by month and group by column using mysql?

I have a database that has two columns - result and time
I'm trying to get a count of how many rows exist of each result in a particular month. There are only two options for result success and failure
I've managed to get a count of how many rows there are in each month, but I can't get the individual count of how many success and how many failure there were in each month.
Here is what I have:
SELECT result, MONTH(time) MONTH, COUNT(*) COUNT
FROM mytable
WHERE YEAR(time)=2017
GROUP BY MONTH(time);
I'm looking for a result that provides me with something like there were 12 successes and 8 failures in a particular month.
Any help would be appreciated.
Use conditional aggregation
SELECT result, MONTH(time) MONTH,
sum(result = 'success') as success_count,
sum(result = 'failure') as failure_count
FROM mytable
WHERE YEAR(time) = 2017
GROUP BY result, MONTH(time);
I would use the following query:
SELECT,
DATE_FORMAT(time, '%m %Y') AS month_year,
SUM(CASE WHEN result = 'success' THEN 1 ELSE 0 END) AS success_count,
SUM(CASE WHEN result = 'failure' THEN 1 ELSE 0 END) AS failure_count
FROM mytable
WHERE YEAR(time) = 2017
GROUP BY
DATE_FORMAT(time, '%m %Y')
Note that you should be aggregating by time period alone, and not by the result, which instead is part of the sum in the CASE expression.

Get percentage of total when using GROUP BY in SQL query

I have a SQL query that I'm using to return the number of training sessions recorded by a client on each day of the week (during the last year).
SELECT COUNT(*) total_sessions
, DAYNAME(log_date) day_name
FROM programmes_results
WHERE log_date >= DATE_SUB(CURDATE(), INTERVAL 1 YEAR)
AND log_date <= CURDATE()
AND client_id = 7171
GROUP
BY day_name
ORDER
BY FIELD(day_name, 'MONDAY', 'TUESDAY', 'WEDNESDAY', 'THURSDAY', 'FRIDAY', 'SATURDAY', 'SUNDAY')
I would like to then plot a table showing these values as a percentage of the total, as opposed to as a 'count' for each day. However I'm at a bit of a loss as to how to do that without another query (which I'd like to avoid).
Any thoughts?
Use a derived table
select day_name, total_sessions, total_sessions / sum(total_sessions) * 100 percentage
from (
query from your question goes here
) temp
group by day_name, total_sessions
You can add the number of trainings per day in your client application to get the total count. This way you definitely avoid having a 2nd query to get the total.
Use the with rollup modifier in the query to get the total returned in the last row:
...GROUP BY day_name WITH ROLLUP ORDER BY ...
Use a subquery to return the overall count within each row
SELECT ..., t.total_count
...FROM programmes_results INNER JOIN (SELECT COUNT(*) as total_count FROM programmes_results WHERE <same where criteria>) as t --NO join condition
...
This will have the largest performance impact on the database, however, it enables you to have the total number in each row.

Mysql Frequency within date range

I have a MySQL DB as such:
Date Customer_ID
How can I turn it into:
Customer_ID | Count_Visits_Past_Week | Count_Visits_Past_Month | Count_Visits_Past_90days | Count_Total
Note : Count_Total =sum of the other three counts
Thanks
The first step is to determine the demarcation points for the specified date ranges.
There's several questions to answer here: did you want to compare just the DATE ('yyyy-mm-dd') and disregard any time component?
By "past week", does that mean within the last seven days, or does it mean so far since the previous Sunday, or does it mean the last last full week, from Sunday through Saturday.
For "past month", does that mean the previous whole month, from the first through the end of the month? Or does it mean that if the query is run on the 20th of the month, we want dates since the 20th of the previous month up until today? Or yesterday?
Once we know the points in time that begin and end each specified period, relative to today's date, we can build expressions that evaluate to those dates.
For example, "past week" could be represented as the most recent seven day period:
DATE(NOW())-INTERVAL 1 WEEK -thru- DATE(NOW())
And "past month" can be represented as the same "day of month" (e.g. 17th) of the immediately preceding month up until today:
DATE(NOW())-INTERVAL 1 MONTH -thru- DATE(NOW())
That's really the first step, to determine the begin and end dates of each specified period.
Once we have that, we can move on to building a query that gets a "count" of rows with a date column that falls within each period.
The "trick" is to use conditional tests in expressions in the SELECT list of the query. We'll use those conditional tests to return a 1 if the row is to be included in the "count", and return 0 or NULL if the row should be excluded.
I prefer to use the SUM() aggregate function to get the "count". But it's also possible to use COUNT() aggregate. (If we use COUNT(), we need to use an expression that returns NULL when the row is to be excluded. I prefer to return a 1 or 0; I think it makes debugging easier.
Here's an outline of what a "count" query would look like.
SELECT t.Customer_Id
, SUM(IF( <some_condition> ,1,0) AS Count_something
, SUM(IF( <other_condition> ,1,0) AS Count_something_else
FROM mytable t
GROUP BY t.Customer_Id
When <some_condition> is true, we return a 1, otherwise we return 0.
To test the conditional expressions, it's often easiest to avoid doing the aggregation, and just return the individual rows:
That way, we can see which individual rows are going to be included in each "count".
For example:
SELECT t.Customer_ID
, t.date
, IF(t.date BETWEEN DATE(NOW())-INTERVAL 1 WEEK AND DATE(NOW()),1,0)
AS visit_past_week
, IF(t.date BETWEEN DATE(NOW())-INTERVAL 1 MONTH AND DATE(NOW()),1,0)
AS visit_past_month
FROM mytable t
ORDER BY t.date, t.Customer_Id
That query doesn't return the "count", it just returns the results of the expressions, which can be useful in testing. And of course we want to test the expressions that return the beginning and ending date of each period:
SELECT DATE(NOW()) - INTERVAL 1 WEEK AS past_week_begin
, DATE(NOW()) AS past_week_end
With this approach, the same row can be included in multiple "counts" with one query and one pass through the table.
Note that the expressions inside the SUM() aggregate in the query below are taking advantage of a convenient shorthand, an expression evaluated as a boolean will return 1 if TRUE, 0 if false, or a NULL.
To use the COUNT aggregate, we need to insure that the expression being aggregated returns a non-NULL when the row is to be "counted", and a NULL when the row is to be excluded from the count. So we use the convenient NULLIF function to return NULL if the value returned by the expression is a zero.
SELECT t.Customer_ID
, COUNT(NULLIF( t.date BETWEEN DATE(NOW())-INTERVAL 1 WEEK AND DATE(NOW()),0))
AS Count_Visits_Past_Week
, COUNT(NULLIF( t.date BETWEEN DATE(NOW())-INTERVAL 1 MONTH AND DATE(NOW()),0))
AS Count_Visits_Past_Month
, COUNT(NULLIF( t.date BETWEEN DATE(NOW())-INTERVAL 90 DAY AND DATE(NOW()),0))
AS Count_Visits_Past_90days
, COUNT(NULLIF( t.date BETWEEN DATE(NOW())-INTERVAL 1 WEEK AND DATE(NOW()),0))
+ COUNT(NULLIF( t.date BETWEEN DATE(NOW())-INTERVAL 1 MONTH AND DATE(NOW()),0))
+ COUNT(NULLIF( t.date BETWEEN DATE(NOW())-INTERVAL 90 DAY AND DATE(NOW()),0))
AS Count_Total
FROM mytable t
GROUP BY t.Customer_Id
NOTE: NULLIF(a,b) is a convenient shorthand for IF a IS NULL THEN return b ELSE return a
Returning the Count_Total is a bit odd, since it's got the potential to "count" the same row multiple times... but the value it returns should match the total of the individual counts.
I think this will give you what you want.
select customer_id,
sum(case when splitter = 'week' then num_visits else 0 end) as visits_this_week,
sum(case when splitter = 'month' then num_visits else 0 end) as visits_this_month,
sum(case when splitter = '90days' then num_visits else 0 end) as visits_last_90days,
sum(num_visits) as total
from (select customer_id, 'week' as splitter, count(*) as num_visits
from tbl
where extract(week from date) = extract(week from sysdate())
and extract(year from date) = extract(year from sysdate())
group by customer_id
union all
select customer_id, 'month' as splitter, count(*) as num_visits
from tbl
where extract(month from date) = extract(month from sysdate())
and extract(year from date) = extract(year from sysdate())
group by customer_id
union all
select customer_id, '90days' as splitter, count(*) as num_visits
from tbl
where date between date_sub(sysdate(), interval 90 day) and
sysdate()) x
group by customer_id
sql fiddle example: http://sqlfiddle.com/#!2/a762c/12/0

Getting total using Union All

So here is my situation. I have 1 query that gets results grouped by date then I union all a second query that gets the totals (no grouping by date). My issue is I am calculating the average of fields and when I want to total up the average my numbers don't add up.
Here is my SQLFiddle
Here is my query:
SELECT
t.end,
SUM(CASE WHEN (t.start != t.end) THEN TIMESTAMPDIFF(DAY, t.start, t.end) ELSE 1 END) / COUNT(t.id) as averageTime
FROM store t
GROUP BY t.end
UNION ALL
SELECT
'Total',
SUM(CASE WHEN (t.start != t.end) THEN TIMESTAMPDIFF(DAY, t.start, t.end) ELSE 1 END) as averageTime
FROM store t
Right now the second query just gives the total not the total of the average. Any help is appreciated, thank you.
To clarify since there is some confusion...
I need to get the average per grouped date by how many items were in that group
timeDiff / count(t.id)
Since I am not grouping in the union all query it is doing it as a whole then dividing. I hope that makes more sense.
The first query is correct the data output is as follows:
1, 1.6667, 3 (Those are the averageTime values from the first query)
5.6667 (Should be the total row) Right now I have it out putting 10 that is the total before the first rows were averaged out.
THe first part of the query can probably be written as:
SELECT t.end,
AVG(CASE WHEN t.start != t.end THEN TIMESTAMPDIFF(DAY, t.start, t.end) ELSE 1
END) as averageTime
FROM store t
GROUP BY t.end;
Presumably, you want the averages of the averages -- rather than the overall average. I assume this because your query is calculating the overall average.
One way is the brute force way:
SELECT t.end,
AVG(CASE WHEN t.start != t.end THEN TIMESTAMPDIFF(DAY, t.start, t.end) ELSE 1
END) as averageTime
FROM store t
GROUP BY t.end
UNION ALL
SELECT 'Total', avg(AverageTime)
FROM (SELECT t.end,
SUM(CASE WHEN t.start != t.end THEN TIMESTAMPDIFF(DAY, t.start, t.end) ELSE 1
END) as averageTime
FROM store t
GROUP BY t.end
) t;
Your question is not clear. But, I suppose you need Average. As #samD mentioned,
the second part of you query can be written as
SELECT
'Total',
AVG(CASE WHEN (t.start != t.end) THEN TIMESTAMPDIFF(DAY, t.start, t.end) ELSE 1 END) as averageTime
FROM store t
You're mistakenly assuming that the COUNT(t.id) in the first query represents the total number of rows, whilst it is actually also affected by the GROUP BY clause. If you multiply the separate rows by their number of days the totals also add up. Remove the division by COUNT(t.id) and the totals will add up, and you will correctly have the grouped SUMs as expected.
I'm just not sure how that solves your problem since you're not really clear on that: you simply cannot add all averages together and expect them to add up to a global average, that's not how math works: the sum of averages of arbitrary sized subgroups is rarely equal to the straight unweighted average of all groups together.

Select single column multiple times at differents point in time

I have a simple table with 4 columns - ID, Date, Category, Value.
I have 5 distinct categories that have certain values daily. I would like to select value column at different points in time and display result along with the appropriate category.
This is the code that I'm using:
select
Category,
case when date=DATE_SUB(CURDATE(),INTERVAL 1 DAY) then value else 0 end as Today,
case when date=DATE_SUB(CURDATE(),INTERVAL 1 MONTH) then value else 0 end as "Month Ago",
case when date=DATE_SUB(CURDATE(),INTERVAL 1 Year) then value else 0 end as "Year Ago"
from table
group by category
It's not working. I'm using mysql database but will run the query in SSRS through an ODBC connection.
The problem with your query is that, as written, the case statements need to be embedded in aggregation functions:
select Category,
avg(case when date=DATE_SUB(CURDATE(),INTERVAL 1 DAY) then value end) as Today,
avg(case when date=DATE_SUB(CURDATE(),INTERVAL 1 MONTH) then value end) as "Month Ago",
avg(case when date=DATE_SUB(CURDATE(),INTERVAL 1 Year) then value end) as "Year Ago"
from table
group by category
I chose "avg" since this seems reasonable if there are multiple values and the "value" column is numeric. You might prefer min() or max() to get other values.
Also, I removed the "else 0" clause, so you will see NULL rather than 0 when there is no value.
This type of query is best done with three separate queries:
SELECT 'Today' AS `When`, Category, value FROM `table`
WHERE date = DATE_SUB(CURDATE(),INTERVAL 1 DAY)
UNION ALL
SELECT 'Month Ago' AS `When`, Category, value FROM `table`
WHERE date = DATE_SUB(CURDATE(),INTERVAL 1 MONTH)
UNION ALL
SELECT 'Year Ago' AS `When`, Category, value FROM `table`
WHERE date = DATE_SUB(CURDATE(),INTERVAL 1 YEAR)
try something like this:
SELECT
t1.Category, t1.Value, t2.Value, t3.Value
FROM YourTable t1
LEFT OUTER JOIN YourTable t2 ON t1.Category=t2.Category
AND Date=DATE_SUB(CURDATE(),INTERVAL 1 Month)
LEFT OUTER JOIN YourTable t3 ON t1.Category=t3.Category
AND Date=DATE_SUB(CURDATE(),INTERVAL 1 Year)
WHERE Date=DATE_SUB(CURDATE(),INTERVAL 1 DAY)
this assumes that you have only one row per your interval. if you have multiple rows per interval, you need to decide which value you want to show for that interval (min, max, etc). you then need to aggergate your multiple rows. if this is the case the OP should provide some sample data and expected query output so testing is possible.