I currently have a query that finds all rows (with status=0) that have occurred before now:
SELECT id, COUNT(1) FROM tbl WHERE status = 0 AND date < UNIX_TIMESTAMP() GROUP BY id;
However, now I'd also like to be able to retrieve the values on the other side of this--i.e., I want to get all dates available after and before now, as two distinct values.
Is there any way to optimize this besides simply running two separate queries?
SELECT id
, SUM(date < UNIX_TIMESTAMP()) AS BeforeNow
, SUM(date > UNIX_TIMESTAMP()) AS AfterNow
FROM tbl
WHERE status = 0
GROUP BY id;
date < UNIX_TIMESTAMP() is a boolean expression, which equates to 1 or 0. The SUM of the expression is equal to the amount of times it was true, or its count.
You can do a conditional count.
SELECT id,
COUNT(CASE WHEN date < UNIX_TIMESTAMP() THEN 1 ELSE null END ) ,
COUNT(CASE WHEN date > UNIX_TIMESTAMP() THEN 1 ELSE null END )
FROM tbl GROUP BY id
Related
I am struggling to count all the values that have the same Timestamp. This is how my database looks like:
Let's say I would like to get the amount of orders in May 2013. What is the right Syntax to get this done?
To get a count for a timestamp range, we can compare the timestamp column to a lower and upper bounds, for example:
SELECT COUNT(*)
FROM mytable t
WHERE t.orderdate >= '2013-05-01 00:00:00'
AND t.orderdate < '2013-06-01 00:00:00'
(All orders on or after the first second of May 1st AND before the first second of June.)
We can also do a similar comparison in an expression in the SELECT list, a conditional aggregation pattern:
SELECT SUM(IF(t.orderdate >= '2013-05-01' AND t.orderdate < '2013-06-01',1,0)) AS cnt_may
FROM mytable t
equivalently
SELECT SUM(CASE WHEN DATE_FORMAT(t.orderdate,'%Y-%m') = '2013-05' THEN 1 ELSE 0 END) AS cnt_may
FROM mytable t
Note that the first query (with conditions in the WHERE clause on the bare orderdate column) can take advantage of an index that has orderdate as the leading column, to perform an efficient range scan operation.
I am working with a query where I want to display number of upcoming dates. The following query returns 0 even though there are dates greater than current date. Please help me to solve this problem.
SELECT (case when b.booked_date > cast(now() as date) then sum(1) else sum(0) end) as upcoming_booked_facilities
from svk_apt_book_facilities b
where b.customer_id = 1
and b.association_id = 1
and b.is_active = 1
group by b.facility_id
You need to sum a CASE expression to do conditional aggregation:
SELECT
facility_id,
SUM(CASE WHEN booked_date > CURDATE() THEN 1 ELSE 0 END) AS upcoming_booked_facilities
FROM svk_apt_book_facilities
WHERE
customer_id = 1 AND
association_id = 1 AND
is_active = 1
GROUP BY
facility_id;
You were trying to use the sum as the predicate of the CASE expression, which is probably not what you want. Note that I am also selecting the facility_id, since you are grouping by that column. If you instead want a conditional sum over the entire table, then don't select or group by facility.
The first case statement i got the correct result but in the second one
Why i got an NULL result Where my second case statement the counter = 2
this is the result i have an image
Query Result that i got Null data in second statement when i grouped by on my date
SELECT DISTINCT date,log,
CASE
WHEN note = 'HOLIDAY' AND counter = 1
THEN 'HOLIDAY'
END note1,
CASE
WHEN note = 'HOLIDAY' AND counter = 2
THEN 'HOLIDAY'
END note2,
FROM timesheet
WHERE timesheet.empid='40' AND date <= CURDATE() AND YEAR(date)= YEAR(CURDATE())
AND MONTH(date) = MONTH(CURDATE())
GROUP BY date
ORDER BY date DESC;
You're using GROUP BY wrong. The rule is that each column in your SELECT clause is either also in your GROUP BY clause or an aggregate function (like count, min, max, avg) must be applied to it.
When you don't follow this rule, a random row for each group is displayed. In your case, when you really have data with note = 'HOLIDAY' AND counter = 2, the rows for the group might look like this
NULL
HOLIDAY
NULL
NULL
but after collapsing (when it's outputted by the select), just the first row is displayed, therefore the NULL value.
Try it like this:
SELECT date,
MIN(log), /*or maybe you want to group by this column, too? */
MAX(CASE
WHEN note = 'HOLIDAY' AND counter = 1
THEN 'HOLIDAY'
END) note1,
MAX(CASE
WHEN note = 'HOLIDAY' AND counter = 2
THEN 'HOLIDAY'
END) note2,
FROM timesheet
WHERE timesheet.empid='40' AND date <= CURDATE() AND YEAR(date)= YEAR(CURDATE())
AND MONTH(date) = MONTH(CURDATE())
GROUP BY date
ORDER BY date DESC;
Also note, that I removed the DISTINCT. Your GROUP BY already does that.
When I run this query I have this error message on phpmydamin: Unknown column 'timestamp' in 'having clause'
My column name is timestamp
SELECT DISTINCT (
hash
) AS total
FROM behaviour
HAVING total =1 and date(timestamp) = curdate()
How to get the number of hash for today?
Use where. And parentheses are not appropriate for select distinct (distinct is not a function). I suspect that you intend:
SELECT COUNT(DISTINCT hash) AS total
FROM behaviour
WHERE date(timestamp) = curdate();
It is better to write the WHERE clause without using a function on the column:
SELECT COUNT(DISTINCT hash) AS total
FROM behaviour
WHERE timestamp >= curdate() AND timestamp < date_add(curdate, interval 1 day);
Although more complicated, it allows the database engine to use an index on behaviour(timestamp) (or better yet, on behaviour(timestamp, hash).
EDIT:
If you want the hash that only appear once, one method is a subquery:
select count(*)
from (select hash
from behaviour
where timestamp >= curdate() AND timestamp < date_add(curdate, interval 1 day)
group by hash
having count(*) = 1
);
To count the hash values only existing once:
select count(*)
from
(
select hash
from behavior
where date(timestamp) = curdate()
group by hash
having count(*) = 1
) dt
The inner select (derived table) will return the hash values only existing once. The outer select will count those rows.
The following code is producing a list with the non-expired rows on top, then the ones with unknown expiry date and at the end the already expired (all of them in ascending order). The problem is that I want the last block of already expired rows to be in descending order so it displays the rows that expired more recently on top of that block without altering the order of the other top blocks.
Basically, I am trying to find a way to incorporate two "ORDER BY" clauses within the same recordset...
Any ideas? Thanks
SELECT *
FROM prueba
WHERE UPPER(CONCAT(Company,Deal,keywords,Type,Expiry,Name)) LIKE UPPER(%s)
ORDER BY (CASE
WHEN prueba.Expiry = 'UNKNOWN' THEN 1
WHEN prueba.Expiry < CURRENT_DATE THEN 2
END)
, prueba.Expiry ASC
Try this
DEMO FIDDLE
SELECT * FROM t
order by case
when expiry = 'Unknown' Then 1
WHEN expiry >= CURRENT_DATE THEN 0
ELSE 2 END,
CASE WHEN expiry >= CURRENT_DATE THEN expiry END,
CASE WHEN expiry < CURRENT_DATE THEN expiry END desc
Separate those records that have expired into another SELECT clause, then UNION ALL:
(SELECT *
FROM prueba
WHERE UPPER(CONCAT(Company,Deal,keywords,Type,Expiry,Name)) LIKE UPPER(%s)
AND prueba.Expiry > CURRENT_DATE
ORDER BY prueba.Expiry DESC)
UNION ALL
(SELECT *
FROM prueba
WHERE UPPER(CONCAT(Company,Deal,keywords,Type,Expiry,Name)) LIKE UPPER(%s)
AND prueba.Expiry = 'UNKNOWN')
UNION ALL
(SELECT *
FROM prueba
WHERE UPPER(CONCAT(Company,Deal,keywords,Type,Expiry,Name)) LIKE UPPER(%s)
AND prueba.Expiry < CURRENT_DATE
ORDER BY prueba.Expiry DESC)