mysql fill missing values with 0 - mysql

I have a mysql query that sometimes results in missing values. For my dashboard I'd like to fill those values, but would prefer to avoid build dummy tables if I can.
query:
SELECT COUNT(Comms_Timestamp) as call_count,DAYOFWEEK(Comms_Timestamp) as bucket
FROM tblTest GROUP BY bucket;
results in
+------------+--------+
| call_count | bucket |
+------------+--------+
| 4 | 1 |
| 7 | 2 |
| 7 | 3 |
| 1 | 5 |
| 6 | 6 |
| 1 | 7 |
+------------+--------+
In the above example you can see bucket 4 is missing. I consider the method where the join is to a select union array, however since both fields are aggregates, I'm not sure how to go about it.
test data is
+---------------------+
| Comms_Timestamp |
+---------------------+
| 2018-12-24 06:04:05 |
| 2018-12-24 12:18:39 |
| 2018-12-21 04:24:31 |
| 2018-12-21 08:32:44 |
| 2018-12-30 01:41:06 |
| 2018-12-30 01:53:00 |
| 2018-12-30 01:53:39 |
| 2018-12-30 02:00:01 |
| 2018-12-17 15:55:03 |
| 2018-12-17 16:04:12 |
| 2018-12-17 16:05:41 |
| 2018-12-17 16:07:43 |
| 2018-12-17 16:10:25 |
| 2018-12-18 14:03:22 |
| 2018-12-18 14:03:29 |
| 2018-12-18 14:10:19 |
| 2018-12-18 14:10:29 |
| 2018-12-18 14:10:31 |
| 2018-12-18 14:10:47 |
| 2018-12-18 14:10:55 |
| 2018-12-20 08:21:07 |
| 2018-12-28 11:03:59 |
| 2018-12-28 12:06:40 |
| 2018-12-28 12:15:01 |
| 2018-12-28 14:29:24 |
| 2019-01-05 13:33:43 |
+---------------------+

Since you are using mysql and don't have access to the seq_ option, here is an alternative way:
SELECT A.x AS bucket, IF(ISNULL(COUNT(t2.Comms_Timestamp)), 0, COUNT(t2.Comms_Timestamp)) AS call_count FROM
(select 1 x union select 2 union select 3 union select 4 union select 5 union select 6 union select 7) AS A
LEFT JOIN tblTest AS t2 ON DAYOFWEEK(t2.Comms_Timestamp) = A.x
GROUP BY bucket
ORDER BY bucket;
It may not be the prettiest option but will do what you need.
Here is a db fiddel link: db<>fiddle

If you are using MariaDB there is their Sequence Storage Engine
There is no create needed for this table, however the maximum value must be known.
select version();
| version() |
| :------------------------------------------ |
| 10.3.11-MariaDB-1:10.3.11+maria~stretch-log |
create table bob (a int)
✓
insert into bob values (4),(2)
✓
select * from seq_1_to_5
| seq |
| --: |
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
SELECT s.seq, bob.a
FROM seq_1_to_5 s
LEFT JOIN bob
ON bob.a = s.seq
ORDER BY s.seq
seq | a
--: | ---:
1 | null
2 | 2
3 | null
4 | 4
5 | null
db<>fiddle here

You can use IFNULL() function in MYSQL:
SELECT IFNULL(COUNT(C.Comms_Timestamp),0) as call_count,IFNULL(DAYOFWEEK(C.Comms_Timestamp),0) as bucket
FROM tblCommunication as C LEFT JOIN tblCareTeam as CT on C.id_Case = CT.id_Case
GROUP BY CT.id_Site,bucket
HAVING CT.id_Site=8;

Related

What is the execution order of the associative sub-query SQL?

I came across this SQL at work, This was written by my colleague. Although there are better solutions, I’m just curious,and now I have simplified it as follows:
-- Calculate the total of the 'APPROVING' salary and the 'AGENT' salary already actual paid
SELECT ifnull(sum(l.salary),0) +
(SELECT ifnull(sum(l1.salary),0)
FROM salary_header h1 JOIN salary_lines l1
ON h1.salary_id = l1.salary_id
WHERE h1.status='APPROVING' AND l1.project_id = l.project_id)
FROM salary_pay_headers h JOIN salary_pay_lines l
ON h.salary_pay_id = l.salary_pay_id
WHERE h.pay_type='AGENT'
AND l.project_id=9904
mysql> select * from salary_header;
+-----------+-----------+
| salary_id | status |
+-----------+-----------+
| 1 | APPROVING |
| 2 | PAID |
+-----------+-----------+
mysql> select * from salary_lines;
+----------------+-----------+------------+--------+
| salary_line_id | salary_id | project_id | salary |
+----------------+-----------+------------+--------+
| 1 | 2 | 9905 | 200.00 |
+----------------+-----------+------------+--------+
mysql> select * from salary_pay_headers;
+---------------+----------+
| salary_pay_id | pay_type |
+---------------+----------+
| 1 | AGENT |
| 2 | OTHER |
+---------------+----------+
mysql> select * from salary_pay_lines;
+--------------------+---------------+------------+--------+
| salary_pay_line_id | salary_pay_id | project_id | salary |
+--------------------+---------------+------------+--------+
| 1 | 1 | 9904 | 3.05 |
| 2 | 1 | 9904 | 201.37 |
| 3 | 1 | 9904 | 6.10 |
| 4 | 1 | 9904 | 10.17 |
| 5 | 1 | 9904 | 6.44 |
| 6 | 1 | 9904 | 9.15 |
| 8 | 3 | 9905 | 100.00 |
+--------------------+---------------+------------+--------+
Its result is not 3.05+201.37+6.10+10.17+6.44+9.15=236.28 as I expected,but 236.28+200=436.28,obviously that one in the salary_line is not filtered out. I have spent the whole afternoon on this problem, so I really want to know the execution order of this SQL.

Remove duplicates leaving at least one with highest parameter from group

I have following schema:
+--+------+-----+----+
|id|device|token|cash|
+--+------+-----+----+
column device is unique and token is not unique and null by default.
What i want to achieve is to set all duplicate token values to default (null) leaving only one with highest cash. If duplicates have same cash leave first one.
I have heard about cursor, but it seems that it can be done with usual query.
I have tried following SELECT only to see if im right about my thought how to achieve this, but it seems im wrong.
SELECT
*
FROM
db.table
WHERE
db.table.token NOT IN (SELECT
*
FROM
(
SELECT DISTINCT
MAX(db.table.balance)
FROM
db.table
GROUP BY db.table.balance) temp
)
For example:
This table after query
+-----+---------+--------+-------+
| id | device | token | cash|
+-----+---------+--------+-------+
| 1 | dev_1 | tkn_1 | 3 |
| 2 | dev_2 | tkn_1 | 10 |
| 3 | dev_3 | tkn_2 | 10 |
| 4 | dev_4 | tkn_2 | 14 |
| 5 | dev_5 | tkn_3 | 10 |
| 6 | dev_6 | null | 10 |
| 7 | dev_7 | null | 10 |
| 8 | dev_8 | tkn_4 | 11 |
| 8 | dev_8 | tkn_4 | 11 |
| 8 | dev_8 | tkn_5 | 11 |
+-----+---------+--------+-------+
should be:
+-----+---------+--------+-------+
| id | device | token | cash|
+-----+---------+--------+-------+
| 1 | dev_1 | null | 3 |
| 2 | dev_2 | tkn_1 | 10 |
| 3 | dev_3 | null | 10 |
| 4 | dev_4 | tkn_2 | 14 |
| 5 | dev_5 | tkn_3 | 10 |
| 6 | dev_6 | null | 10 |
| 7 | dev_7 | null | 10 |
| 8 | dev_8 | tkn_4 | 11 |
| 8 | dev_8 | null | 11 |
| 8 | dev_8 | tkn_5 | 15 |
+-----+---------+--------+-------+
Thanks in advance :)
Try using an EXISTS subquery:
UPDATE yourTable t1
SET token = NULL
WHERE EXISTS (SELECT 1 FROM (SELECT * FROM yourTable) t2
WHERE t2.token = t1.token AND
t2.cash > t1.cash);
Demo
Note that this answer assumes that there would never be a tie for two token records having the same highest cash amount.
To set exactly one row in the even of duplicates on the maximum cash, use the id:
update t join
(select tt.*,
(select t3.id
from t t3
where t3.token = tt.token
order by t3.cash desc, id desc
) as max_cash_id
from t tt
) tt
on t.id = tt.id and t.id < tt.max_cash_id
set token = null;

Sum of all rows prior to (and including) date on current row in MYSQL

It's important to know that the date will be unknown during the query time, so I cannot just hard code a 'WHERE' clause.
Here's my table:
+-----------+----------+-------------+
| Date_ID | Customer | Order_Count |
+-----------+----------+-------------+
| 20150101 | Jones | 6 |
| 20150102 | Jones | 4 |
| 20150103 | Jones | 3 |
+-----------+----------+-------------+
Here's the desired output:
+-----------+----------+------------------+
| Date_ID | Customer | SUM(Order_Count) |
+-----------+----------+------------------+
| 20150101 | Jones | 6 |
| 20150102 | Jones | 10 |
| 20150103 | Jones | 13 |
+-----------+----------+------------------+
My guess is I need to use a variable or perhaps a join.
Edit: still not able to get it fast enough. very slow.
Try this query; it's most likely the best you can do without limiting the dataset you operate on. It should benefit from an index (customer, date_id).
select
t1.date_id, t1.customer, sum(t2.order_count)
from
table1 t1
left join
table1 t2 on t1.customer = t2.customer
and t1.date_id >= t2.date_id
group by
t1.date_id, t1.customer;
Sample SQL Fiddle.
One way you could go about it is by using a sub query which sums all orders up till the current order. Probably not the fastest way, but it should do the trick.
SELECT `Date_ID`, `Customer`,
(SELECT sum(b.`Order_Count`)
FROM tablename as b WHERE
b.`Date_ID` <= a.`Date_ID` AND
a.`customer = b.`Customer`)
FROM tablename as a
Where performance is an issue, consider a solution akin the following:
SELECT * FROM ints;
+---+
| i |
+---+
| 0 |
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
| 6 |
| 7 |
| 8 |
| 9 |
+---+
SELECT i,#i:=#i+i FROM ints, (SELECT #i:=0)n ORDER BY i;
+---+----------+
| i | #i:=#i+i |
+---+----------+
| 0 | 0 |
| 1 | 1 |
| 2 | 3 |
| 3 | 6 |
| 4 | 10 |
| 5 | 15 |
| 6 | 21 |
| 7 | 28 |
| 8 | 36 |
| 9 | 45 |
+---+----------+
you can consider this solution
select Date_ID,
Customer,
SUM(Order_COunt) over (order by Date_ID, Customer rows unbounded preceding) as SUM(Order_COunt)
from table

Select most recent MAX() and MIN() - WebSQL

i'm build an exercises web app and i'm working with two tables like this:
Table 1: weekly_stats
| id | code | type | date | time |
|----|--------------|--------------------|------------|----------|
| 1 | CC | 1 | 2015-02-04 | 19:15:00 |
| 2 | CC | 2 | 2015-01-28 | 19:15:00 |
| 3 | CPC | 1 | 2015-01-26 | 19:15:00 |
| 4 | CPC | 1 | 2015-01-25 | 19:15:00 |
| 5 | CP | 1 | 2015-01-24 | 19:15:00 |
| 6 | CC | 1 | 2015-01-23 | 19:15:00 |
| .. | ... | ... | ... | ... |
Table 2: global_stats
| id | exercise_number |correct | wrong |
|----|-----------------|--------|-----------|
| 1 | 138 | 1 | 0 |
| 2 | 246 | 1 | 0 |
| 3 | 988 | 1 | 10 |
| 4 | 13 | 5 | 0 |
| 5 | 5 | 4 | 7 |
| 6 | 5 | 4 | 7 |
| .. | ... | ... | ... |
What i would like is to get MAX(correct-wrong) and MIN(correct-wrong) and now i'm working with this query:
SELECT
exercise_number,
date,
time
FROM weekly_stats AS w JOIN global_stats AS g
ON w.id=g.id
WHERE correct - wrong = (SELECT MAX(correct - wrong) from global_stats)
UNION
SELECT
exercise_number,
date,
time
FROM weekly_stats AS w JOIN global_stats AS g
ON w.id=g.id
WHERE correct - wrong = (SELECT MIN(correct - wrong) from global_stats);
This query is working good, except for one thing: when "WHERE correct - wrong = (SELECT MIN(correct - wrong)[...]" selects more than one row, the row selected is the first but i would like to have returned the most recent (in other words: ordered by datetime(date, time)). Is it possible?
Thanks!
I think you can solve it like this:
SELECT * FROM (
SELECT
1 as sort_column,
exercise_number,
date,
time
FROM weekly_stats AS w JOIN global_stats AS g
ON w.id=g.id
WHERE correct - wrong = (SELECT MAX(correct - wrong) from global_stats)
ORDER BY date DESC, time DESC
LIMIT 1 ) as a
UNION
SELECT * FROM (
SELECT
2 as sort_column,
exercise_number,
date,
time
FROM weekly_stats AS w JOIN global_stats AS g
ON w.id=g.id
WHERE correct - wrong = (SELECT MIN(correct - wrong) from global_stats)
ORDER BY date DESC, time DESC
LIMIT 1) as b
ORDER BY sort_column;
Here is the documentation about how UNION works.

ORDER BY complex

I've a mysql_query: select * from table ORDER BY v1, v2 ASC
Can be made a query to sort v1, v2 as below ?
+---------------+-----------------------+------+-----+
| id | name | v1 | v2 |
+---------------+-----------------------+------+-----+
| 1 | a | 1 | A |
| 2 | a | 2 | B |
| 3 | a | 3 | C |
| 4 | a | 1 | A |
| 5 | a | 2 | B |
| 6 | a | 3 | C |
| 7 | a | 1 | A |
| 7 | a | 2 | B |
| 7 | a | 3 | C |
+---------------+-----------------------+------+-----+
SQL fiddle
You need another column to sort like that. You have to tell MySQL why ids 1,2,3 come before 4,5,6. If you have another column that is e.g. 1 for 1,2,3, 2 for 4,5,6 etc, you can sort with:
ORDER BY missing_col, v1, v2
You can try this:
Select id, name, v1, v2, (v1 + ASCII(v2)) as mySum
from table
order by mySum
ASCII http://dev.mysql.com/doc/refman/5.0/en/string-functions.html#function_ascii