MySQL Group By Sum by Day - mysql

I have a table with
id int pk auto_inc | created int(11) | amount int | user_id int
I want to create a list of rows grouped by day totalling the amount field.
I have tried this:
SELECT created, sum(amount) as amount, id FROM total_log WHERE user_id = $this->user_id GROUP BY DAY(created)
This doesn't give the right results. They are getting grouped into one row.
The date is saved from dd/mm/yyyy format to unix time stamp like 1349046000

SELECT
DATE(FROM_UNIXTIME(created)) as d,
sum(amount) as amount
FROM total_log
WHERE user_id = $this->user_id
GROUP BY d

MySQL doesn't like mixing day and int columns:
mysql> select day(1349046000);
+-----------------+
| day(1349046000) |
+-----------------+
| NULL |
+-----------------+
1 row in set, 1 warning (0.00 sec)
mysql> show warnings;
+---------+------+----------------------------------------+
| Level | Code | Message |
+---------+------+----------------------------------------+
| Warning | 1292 | Incorrect datetime value: '1349046000' |
+---------+------+----------------------------------------+
1 row in set (0.00 sec)
So all of your rows will have NULL for day(some_int_value), and they'll all be in the same group.
I would suggest using a date or datetime type for that column instead.
Also, columns not in the group by clause should not be referenced in the select statement, unless an aggregating function is used on them.

try
SELECT
DAY(DATE(FROM_UNIXTIME(created))),
sum(amount) as amount
FROM total_log
WHERE user_id = $this->user_id
GROUP BY DAY(DATE(FROM_UNIXTIME(created)))

Related

Add column with count(*)

When I do "select count(*) from users", it returns the data in the following format:
mysql> select count(*) from users;
+----------+
| count(*) |
+----------+
| 100 |
+----------+
1 row in set (0.02 sec)
I would like to get the data in the following format instead.
+---------+----------+
| key | count |
+---------+----------+
| my_count| 100 |
+---------+----------+
The reason is to feed this data to a pre-built widget which expects the data in the above format.
Is there a way to do this in SQL?
I tried various options such as "group by" but couldn't get it working.
mysql> select count(*) from users;
+---------+----------+
| key | count |
+---------+----------+
| my_count| 100 |
+---------+----------+
Just add a string literal to your select clause:
SELECT 'my_count' AS `key`, COUNT(*) AS count
FROM users;
Note that key is a reserved keyword in MySQL, so we must escape it using backticks.
If you intended to use GROUP BY, then you probably want a query like this:
SELECT `key`, COUNT(*) AS count
FROM users
GROUP BY `key`;

mysql date_format is not working returning empty

I am having problem while getting the result i want from database, DATE_FORMAT function is returning null instead or returning a formatted date.
table columns and their datatype:
paid_amount -> float
created_at -> varchar(100)
bellow is the query is:
SELECT SUM(paid_amount) AS amount,
date_format(created_at,'%Y-%m-%d') as dated
FROM `job_card`
WHERE date_format(created_at,'%Y-%m-%d') >= '2017-09-03' AND
date_format(created_at,'%Y-%m-%d') <= '2017-10-03' GROUP BY date(created_at)
I am using another approach of converting date to time stamp using UNIX_TIMESTAMP function but the still getting the same issue:
SELECT SUM(paid_amount) AS paid_amount,
UNIX_TIMESTAMP(created_at) AS duration
FROM job_card
WHERE UNIX_TIMESTAMP(created_at) >= 1504549800 AND
UNIX_TIMESTAMP(created_at) <= 1507055400 GROUP BY date(payment_date)
You can't use the date functions on a varchar field. You'll need to convert created_at to a date field.
That will make your SQL a lot cleaner as well as you can just use clauses like created_at > '2017-09-03' directly in your query.
I don't know how your dates are formatted currently as strings, but your best bet is probably to write something to read the dates out, format them as YYYY-MM-DD, and re-insert them. If all the dates are that format you should be able to convert that field to date type and keep the information intact.
mysql> desc test;
+--------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------+-------------+------+-----+---------+-------+
| field1 | varchar(10) | YES | | NULL | |
+--------+-------------+------+-----+---------+-------+
1 row in set (0.00 sec)
mysql> select * from test;
+------------+
| field1 |
+------------+
| 2017-01-01 |
+------------+
1 row in set (0.00 sec)
mysql> alter table test modify field1 date;
Query OK, 1 row affected (0.24 sec)
Records: 1 Duplicates: 0 Warnings: 0
mysql> desc test;
+--------+------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------+------+------+-----+---------+-------+
| field1 | date | YES | | NULL | |
+--------+------+------+-----+---------+-------+
1 row in set (0.00 sec)
mysql> select * from test;
+------------+
| field1 |
+------------+
| 2017-01-01 |
+------------+
1 row in set (0.00 sec)

Column count of is null plus count of is not null do not add up

Trying to see what's in a column I ended up doing some counting.
The table has 3981 rows.
But the counted column only shows a much lower number in total of its null and non null values.
How come ?
MariaDB [mydb]> select count(naf) from client where naf is not null;
+------------+
| count(naf) |
+------------+
| 83 |
+------------+
1 row in set (0.01 sec)
MariaDB [mydb]> select count(naf) from client where naf is null;
+------------+
| count(naf) |
+------------+
| 0 |
+------------+
1 row in set (0.01 sec)
MariaDB [mydb]> select count(*) from client;
+----------+
| count(*) |
+----------+
| 3981 |
+----------+
1 row in set (0.01 sec)
The following query is misleading you:
select count(naf) from client where naf is null;
The COUNT function ignores all NULL values. Hence, this query would never return any value other than zero. In reality, there are 3898 NULL records in the client table. To count nulls, you can try using the SUM function instead:
SELECT SUM(1) FROM client WHERE naf IS NULL;
This should be returning a sum of 3898.

MySQL inquiry does not make difference when using order by

I create a table like:
CREATE TABLE my_table
(
value int(20)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
And I insert some data:
mysql> SELECT * FROM my_table;
+-------+
| value |
+-------+
| 0 |
| 1 |
| 2 |
| 3 |
+-------+
When I execute SELECT COUNT(value), value FROM my_table; and SELECT COUNT(value), value FROM my_table ORDER BY value DESC;, they both show:
+--------------+-------+
| COUNT(value) | value |
+--------------+-------+
| 4 | 0 |
+--------------+-------+
My question is: why the column at the right side is always 0? Why ORDER BY value DESC doesn't make any difference here?
ORDER BY is processed after it generates the results. When you use an aggregate function like COUNT() without GROUP BY, it aggregates all the selected rows, and this produces one row of results. Any non-aggregated columns come from indeterminate rows; the ORDER BY clause has no effect on how this row is selected.
ORDER BY sorts the result rows. What you are looking for is MAX() or MIN() on the value
SELECT COUNT(value), MAX(value) FROM my_table;
SELECT COUNT(value), MIN(value) FROM my_table;

Get random posts without scanning the whole database [duplicate]

This question already has answers here:
Fetching RAND() rows without ORDER BY RAND() in just one query
(3 answers)
Closed 9 years ago.
How can I get random posts without scanning the whole database.
As I know if you use MySQL ORDER BY RAND() it will scan the whole database.
If there is any other way to do this without scanning the whole database.
A tiny modification of #squeamish ossifrage solution using primary key values - assumming that there is a primary key in a table with numeric values:
SELECT *
FROM delete_me
WHERE id >= Round( Rand() *
( SELECT Max( id ) FROM test ))
LIMIT 1
For table containing more than 50.000 rows the query runs in a 100 miliseconds:
mysql> SELECT id, table_schema, table_name
FROM delete_me
WHERE id >= Round( Rand() *
( SELECT Max( id ) FROM delete_me ))
LIMIT 1;
+-----+--------------------+------------+
| id | table_schema | table_name |
+-----+--------------------+------------+
| 173 | information_schema | PLUGINS |
+-----+--------------------+------------+
1 row in set (0.01 sec)
A lot of people seem to be convinced that ORDER BY RAND() is somehow able to produce results without scanning the whole table.
Well it isn't. In fact, it's liable to be slower than ordering by column values, because MySQL has to call the RAND() function for each row.
To demonstrate, I made a simple table of half a million MD5 hashes:
mysql> select count(*) from delete_me;
+----------+
| count(*) |
+----------+
| 500000 |
+----------+
1 row in set (0.00 sec)
mysql> explain delete_me;
+-------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+------------------+------+-----+---------+----------------+
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| txt | text | NO | | NULL | |
+-------+------------------+------+-----+---------+----------------+
2 rows in set (0.12 sec)
mysql> select * from delete_me limit 4;
+----+----------------------------------+
| id | txt |
+----+----------------------------------+
| 1 | 9b912c03d87991b71955a6cd4f81a299 |
| 2 | f1b7ddeb1c1a14265a620b8f2366a22e |
| 3 | 067b39538b767e2382e557386cba37d9 |
| 4 | 1a27619c1d2bb8fa583813fdd948e94c |
+----+----------------------------------+
4 rows in set (0.00 sec)
Using ORDER BY RAND() to choose a random row from this table takes my computer 1.95 seconds.
mysql> select * from delete_me order by rand() limit 1;
+--------+----------------------------------+
| id | txt |
+--------+----------------------------------+
| 446149 | b5f82dd78a171abe6f7bcd024bf662e8 |
+--------+----------------------------------+
1 row in set (1.95 sec)
But ordering the text fields in ascending order takes just 0.8 seconds.
mysql> select * from delete_me order by txt asc limit 1;
+-------+----------------------------------+
| id | txt |
+-------+----------------------------------+
| 88583 | 00001e65c830f5b662ae710f11ae369f |
+-------+----------------------------------+
1 row in set (0.80 sec)
Since the id values in this table are numbered sequentially starting from 1, I can choose a random row much more quickly like this:
mysql> select * from delete_me where id=floor(1+rand()*500000) limit 1;
+-------+----------------------------------+
| id | txt |
+-------+----------------------------------+
| 37600 | 3b8aaaf88af68ca0c6eccff7e61e897a |
+-------+----------------------------------+
1 row in set (0.02 sec)
But in the general case, I would suggest using the method proposed by Mike in the page linked to by #deceze.
My suggestion for this kind of requirement is to use an MD5 hash.
Add a field to your DB table, CHAR(32), and create and index for it.
Populate it for every record with an MD5 hash of anything (maybe the value from the ID column or just any old random number, doesn't matter too much as long as each record is different)
Now you can query the table like so:
SELECT * FROM myTable WHERE md5Col > MD5(NOW()) LIMIT 1
This will give you a single random record without having to scan the whole table. The table has a random sort order thanks to the MD5 values. MD5 is great for this because it's quick and randomly distributed.
Caveats:
If the MD5 from your SELECT query results in a hash that is larger than the last record in your table, you might get no records from the query. If that happens, you can always re-query it with a new hash.
Having a fixed MD5 hash on each record means that the records are in a fixed order. This isn't really an issue if you're only ever fetching a single record at a time, but if you're using it to fetch groups of records, it may be noticable. You can of course correct this if you want by rehashing records as you load them.