MySQL AND condition parenthesis not working - mysql

The following select statement does not return the correct results, despite furiously moving parenthesis around:
SELECT * FROM Table A where (refId != <some refId> and date <= (now() - INTERVAL 10 DAY))
It seems that the AND condition is short circuiting for some reason, meaning that if the refId does in fact equal the refId in question, the date doesn't matter. This statement needs to return rows without the refId in question but only if the date condition matches as well.
For instance, I have many refIds, but I want to filter out one of them, but only when the date is also outside the 10 day window.
Sample Data: Expected Results:
----------- -----------------
|id|refId| date | refId != 5 AND date <= (now() - INTERVAL 10 DAY)
|1 | 1 | 2018-2-10| Only row 1 should be returned since the
|2 | 5 | 2018-2-14| refId !=5 AND the date is <= 10 days ago.
|3 | 2 | 2018-2-20|
|4 | 5 | 2018-2-22|
|5 | 5 | 2018-2-12|

I think you want = and or:
SELECT *
FROM Table A
WHERE (refId = <some refId> OR date <= (now() - INTERVAL 10 DAY))

Related

Mysql Subtract 'N' days to date value and compare to current date. N and Date are two column values

My table contains remind_me_befor_days column and expiry_date column.
I have to select records with condition (expiry_date - remind_me_befor_days) = current_date.
How this possible by query. Anyone can help me ?
Thanks in advance
You can do date arithemtics:
select t.*
from mytable t
where t.expiry_date - interval t.remind_me_befor_days day = current_date
Demo on DB Fiddle:
-- sample data
select * from mytable;
id | expiry_date | remind_me_befor_days
-: | :---------- | -------------------:
1 | 2019-12-25 | 10
2 | 2019-12-25 | 5
-- query
select t.*, current_date
from mytable t
where t.expiry_date - interval t.remind_me_befor_days day = current_date
id | expiry_date | remind_me_befor_days | current_date
-: | :---------- | -------------------: | :-----------
1 | 2019-12-25 | 10 | 2019-12-15
To split this into a few subproblems, we have:
Find the current date.
Subtract your remind_me_befor_days column value from the expiry_date column.
Use that as a filter for the records you want from the table.
The first can be done using the MySQL CURDATE() function. Read more here. Combining that with the second and third steps, you get something that looks like
SELECT *
FROM my_table
WHERE expiry_date - remind_me_befor_days = CURDATE()
I would strongly recommend that you write the condition as:
where expiry_date = curdate() + interval remind_me_before_days day
This is index friendly, so it can make use of an index on expiry_date.

Get the hourly average amount based on rows with minutely timestamp

I have a table structure that looks like this:
+---------+------------+----------+---------+-----------+------------+
| id | account_id | hashrate | workers | sharerate | timestamp |
+---------+------------+----------+---------+-----------+------------+
| 1227368 | 42 | 405211 | 1 | 6183 | 1534264380 |
| 1227367 | 12 | 450077 | 1 | 6868 | 1534264380 |
+---------+------------+----------+---------+-----------+------------+
A row is created every minute for a user with an active worker.
I'm attempting to display a chart that will show the average hashrate and sharerate for each hour over a 24 hour period.
My sql query so far is:
SELECT
timestamp,
SUM(hashrate) as hashes,
SUM(sharerate) AS shares
FROM statistics_users
WHERE FROM_UNIXTIME(timestamp) >= DATE_SUB(NOW(), INTERVAL 1 DAY)
GROUP BY timestamp;
which returns:
+------------+------------+----------+
| timestamp | hashes | shares |
+------------+------------+----------+
| 1534177980 | 2282744244 | 34831913 |
Which is returning 1440 objects (one for each minute of day). I want to group the items by hour and average all sums returned from my query of minutes into an hour. Not sure where to go from here and I'm fairly new to writing sql queries, so any help would be much appreciated.
We can use an expression that returns a representation of hour, basically trimming off minutes and seconds, and then GROUP BY that expression. One possibility it to use the MySQL DATE_FORMAT function.
SELECT DATE_FORMAT(FROM_UNIXTIME(t.timestamp),'%Y-%m-%d %H') AS ts_hr
, AVG(t.hashes)
, AVG(t.shares)
FROM (
-- original query goes here as inline view
SELECT timestamp
, SUM(hashrate) as hashes
, SUM(sharerate) AS shares
FROM statistics_users
WHERE FROM_UNIXTIME(timestamp) >= NOW() + INTERVAL -1 DAY
GROUP BY timestamp
) t
GROUP
BY DATE_FORMAT(FROM_UNIXTIME(t.timestamp),'%Y-%m-%d %H')
-- ^^^^^^^^^^^^ ^^^^^^^^^^^^^^^
Note that the condition in the WHERE clause doesn't respect hour boundaries, so we're going to get a partial result for first hour, and a partial last hour.
The specification isn't clear, as to whether we want the average of the sums for each timestamp, or more simply
SELECT DATE_FORMAT(FROM_UNIXTIME(t.timestamp),'%Y-%m-%d %H') AS ts_hr
, AVG(t.hashrate)
, AVG(t.sharerate)
FROM statistics_users t
WHERE FROM_UNIXTIME(t.timestamp) >= NOW() + INTERVAL -1 DAY
GROUP
BY DATE_FORMAT(FROM_UNIXTIME(t.timestamp),'%Y-%m-%d %H')
-- ^^^^^^^^^^^^ ^^^^^^^^^^^^^^^
https://dev.mysql.com/doc/refman/8.0/en/date-and-time-functions.html#function_date-format

How can I combine two sql with not exists?

I doubt it can be right to solve.
test_table:
id | name | value | date
---------|---------|---------|------------
1 | john | 32 | 2016-01-08
2 | tom | 590 | 2016-01-03
3 | king | 1903 | 2016-01-01
4 | john | 490 | 2016-01-02
5 | gary | 58 | 2016-01-18
6 | cat | 5 | 2016-01-10
sql1:
select name,sum(value) as val from test_table where val > 500 group by name;
sql2:
select name from test_table where date >= DATE_SUB(CURDATE(),INTERVAL 1 WEEK) group by name;
I want combine two sql in one. The sql result name is not exists in sql2 name result collection.
update:
sorry for my confused describe.
The sql1 is scan whole the table, it aim to find out who's total value is greater than 500.
The sql2 is scan last week's data to find out who exists last week.
So, I Want combine two sql , to find out those people who's total value is greater than 500 but not exists last week.
sorry again for my poor English.
update - add example:
just like the table content,the result should be :
john,tom,king
because their total value is > 500,but not update last week
But how can I do it.
Thanks all.
SELECT name, sum(value)
FROM test_table
WHERE value > 500
AND name NOT IN (
SELECT name
FROM test_table
WHERE `date` >= DATE_SUB(CURDATE(), INTERVAL 1 WEEK)
)
GROUP BY 1
OR
SELECT name, sum(value)
FROM test_table
WHERE value > 500
GROUP BY 1
HAVING max(`date') < DATE_SUB(CURDATE(), INTERVAL 1 WEEK)
If you are trying to having sum(value) > 500, delete above lines
WHERE value > 500
And change AND to WHERE for the first SQL
And add this line at end of each of above:
having sum(value) > 500
It is really helpful to describe what you want, as well as providing SQL samples. You seem to want the sum of values greater than 500 for names that have no row with date in the past week.
If so:
select name, sum(case when value > 500 then value else 0 end)
from test_table
where value > 500
group by name
having max(date) < DATE_SUB(CURDATE(), INTERVAL 1 WEEK) ;

Change DATE_ADD's unit based on column's value

I have a table which stores the datetime an operation was last_run, I also have a frequency column, which is the frequency, in months, that the operation should run.
+----+---------------------+-----------+
| id | last_run | frequency |
+----+---------------------+-----------+
| 1 | 2014-05-22 00:00:00 | 12 |
|----|---------------------|-----------|
| 2 | 2015-05-15 00:00:00 | 0.25 |
+----+---------------------+-----------+
I'm using the following query to obtain the date that the operation should next be run:
SELECT DATE_ADD(last_run, INTERVAL frequency MONTH) AS next_run FROM table;
This fails to return the correct next_run date on the second record because of the frequency value.
Is it possible to add a condition so that when frequency = 0.25
frequency becomes 7
MONTH becomes DAY
This is part of an old system which I am currently refactoring. Would it just be better to store the interval type (DAY/MONTH/YEAR), and pass that to the query, or is there a way of adding the above conditions to the query?
I think you have to use a big case statement. Something like this:
select (case when frequency >= 1
then DATE_ADD(last_run, INTERVAL frequency MONTH)
when frequency < 1
then DATE_ADD(last_run, INTERVAL frequency*28 DAY)
ELSE DATE_ADD(last_run, INTERVAL 1 MONTH) --Adding for may be null values
end)

Mysql: converting date fro 'dd/mm/yyyy' to 'yyyymmdd'

Im working on a database that store dates in a varchar(10) mysql field (so sad).
I can not alter the database structure (im building a small plug-in), but i have to query the database finding rows where this data field is between the next 10 days.
Example:
| fid | fdate |
| 1 | 10/09/2010 |
| 2 | 17/09/2010 |
| 3 | 19/09/2010 |
I have to get the rows with fid 1 and 2, becose the date is <= now + 10 days.
Usually i make this kind of query:
SELECT fid FROM table WHERE fdate <= DATE_ADD(NOW(), INTERVAL 10 DAY);
Or, if the date is in the format `yyyymmdd':
SELECT fid FROM table WHERE fdate <= DATE_FORMAT(NOW(), '%Y%m%d');
But theyre useless with the format dd/mm/yyyy.
I've figured out with
SELECT fid, CONCAT(SUBSTRING(fdate, 7, 4), SUBSTRING(fdate, 4, 2), SUBSTRING(fdate, 1, 2)) AS mydate FROM table HAVING mydate <= DATE_ADD(NOW(), INTERVAL 10 DAY);
but i guess it is a bit an overkill rebuilding the date format with concat and substring, and the having clause wont help the query speed.
Any idea?
EDIT
After Adriano's comment, the right solution is
SELECT fid FROM test WHERE STR_TO_DATE(fdate, '%d/%m/%Y') <= DATE_ADD(NOW(), INTERVAL 10 DAY);
so i can avoid the concat and having clause.
What about using str_to_date() to create a date from your format?
EDIT after reading your comment, I created a table like yours:
mysql> SELECT fid, fdate FROM test;
+------+------------+
| fid | fdate |
+------+------------+
| 1 | 10/9/2010 |
| 2 | 17/9/2010 |
| 3 | 19/09/2010 |
+------+------------+
and then did
mysql> SELECT fid FROM test WHERE STR_TO_DATE(fdate, '%d/%m/%Y') <= DATE_ADD(NOW(), INTERVAL 10 DAY);
+------+
| fid |
+------+
| 1 |
| 2 |
+------+
Seems to work. What exactly do you get?