I have two tables in MySQL that I'm comparing with the following attributes:
tbl_fac : facility_id, chemical_id, criteria
10 , 25 , 50
10 , 26 , 60
10 , 27 , 60
11 , 25 , 30
11 , 27 , 31
etc...
tbl_samp: sample_id, chemical_id, result
5 , 25 , 51
5 , 26 , 61
6 , 25 , 51
6 , 26 , 61
6 , 27 , 500
etc....
These tables are joined by chemical_id (many-to-many---- ugh), and there are several thousand facility_id's, and several hundred chemical_id's for each facility_id. There are also several thousand sample_id's, each with several hundred chemical_id's for each sample_id. All-in-all, there are around 500,000 records in tbl_fac, and 1,000,000+ records in tbl_samp.
I'm trying to extract three groups of sample_id's from this dataset:
Group 1: any sample_id where tbl_samp.result > tbl_fac.criteria (i.e., result exceeds criteria)
Group 2: any sample_id where tbl_samp.result < tbl_fac.criteria, AND all tbl_fac.chemical_id's are present for that sample_id (i.e., result is less than criteria, and everything is there)
Group 3: any sample_id where tbl_samp.result < tbl_fac.criteria, BUT one or more tbl_fac.chemical_id's are missing in the sample_id (i.e., result is less than criteria, but something is missing)
Here's the Question: How do I get all three Groups efficiently in one query?
I've tried:
select *
from tbl_fac
left join tbl_samp
on tbl_fac.chemical_id = tbl_samp.chemical_id
But this only yields values that are missing for the entire dataset (not the individual samples). I have a hackish query working that uses a third table to join tbl_fac and tbl_samp, but it is so ugly I'm actually embarrassed to post it....
As always, many thanks in advance for your thoughts on this one!
Cheers,
Josh
EDIT: Ideally, I would like the sample_id and Group returned -- with just one Group per sample ID (my knowledge of the data indicates that they will always fall into one of the three categories above).
This answer makes the assumption that there is a unique constraint on facility_id and chemical_id in tbl_fac and a unique constraint on sample_id and chemical_id in tbl_samp. What I did was build up the query one step at a time. Whether this is efficient remains to be seen.
Group 1: any sample_id where tbl_samp.result > tbl_fac.criteria (i.e., result exceeds criteria)
SELECT tbl_samp.sample_id,
'ResultsGreaterThanCriteria' AS samplegroup
FROM tbl_fac
INNER JOIN tbl_samp
ON tbl_fac.chemical_id = tbl_samp.chemical_id
WHERE tbl_samp.result > tbl_fac.criteria
GROUP BY tbl_samp.sample_id
Group 2: any sample_id where tbl_samp.result < tbl_fac.criteria, AND all tbl_fac.chemical_id's are present for that sample_id (i.e., result is less than criteria, and everything is there)
SELECT tbl_samp.sample_id,
'ResultLessThanCriteriaAndAllChems' AS samplegroup
FROM tbl_fac
INNER JOIN tbl_samp
ON tbl_fac.chemical_id = tbl_samp.chemical_id
WHERE tbl_samp.result < tbl_fac.criteria
AND NOT EXISTS (SELECT *
FROM tbl_fac tf
LEFT JOIN tbl_samp ts
ON tf.chemical_id = ts.chemical_id
WHERE ts.chemical_id IS NULL
AND tbl_samp.sample_id = ts.sample_id)
GROUP BY tbl_samp.sample_id
Group 3: any sample_id where tbl_samp.result < tbl_fac.criteria, BUT one or more tbl_fac.chemical_id's are missing in the sample_id (i.e., result is less than criteria, but something is missing)
SELECT tbl_samp.sample_id,
'ResultsLessThanCriteriaWithMissingChems' AS samplegroup
FROM tbl_fac
INNER JOIN tbl_samp
ON tbl_fac.chemical_id = tbl_samp.chemical_id
WHERE tbl_samp.result < tbl_fac.criteria
AND EXISTS (SELECT *
FROM tbl_fac tf
LEFT JOIN tbl_samp ts
ON tf.chemical_id = ts.chemical_id
WHERE ts.chemical_id IS NULL
AND tbl_samp.sample_id = ts.sample_id)
GROUP BY tbl_samp.sample_id
And finally, you union all three queries together and get:
SELECT tbl_samp.sample_id,
'ResultsGreaterThanCriteria' AS samplegroup
FROM tbl_fac
INNER JOIN tbl_samp
ON tbl_fac.chemical_id = tbl_samp.chemical_id
WHERE tbl_samp.result > tbl_fac.criteria
GROUP BY tbl_samp.sample_id
UNION ALL
SELECT tbl_samp.sample_id,
'ResultLessThanCriteriaAndAllChems' AS samplegroup
FROM tbl_fac
INNER JOIN tbl_samp
ON tbl_fac.chemical_id = tbl_samp.chemical_id
WHERE tbl_samp.result < tbl_fac.criteria
AND NOT EXISTS (SELECT *
FROM tbl_fac tf
LEFT JOIN tbl_samp ts
ON tf.chemical_id = ts.chemical_id
WHERE ts.chemical_id IS NULL
AND tbl_samp.sample_id = ts.sample_id)
GROUP BY tbl_samp.sample_id
UNION ALL
SELECT tbl_samp.sample_id,
'ResultsLessThanCriteriaWithMissingChems' AS samplegroup
FROM tbl_fac
INNER JOIN tbl_samp
ON tbl_fac.chemical_id = tbl_samp.chemical_id
WHERE tbl_samp.result < tbl_fac.criteria
AND EXISTS (SELECT *
FROM tbl_fac tf
LEFT JOIN tbl_samp ts
ON tf.chemical_id = ts.chemical_id
WHERE ts.chemical_id IS NULL
AND tbl_samp.sample_id = ts.sample_id)
GROUP BY tbl_samp.sample_id
SELECT
sample_id,
IF(result = criteria, -1, /* unspecified behavior */
IF(result > criteria, 1,
IF(nb_chemicals = total_nb_chemicals, 2, 3))) AS grp
FROM (
SELECT s.result, s.sample_id, f.criteria, f.chemical_id,
COUNT(DISTINCT f.chemical_id) AS nb_chemicals
FROM tbl_fac f JOIN tbl_samp s
ON f.chemical_id = s.chemical_id
GROUP BY s.sample_id
) t
CROSS JOIN (
SELECT COUNT(DISTINCT chemical_id) AS total_nb_chemicals
FROM tbl_fac
) u
New solution:
SELECT
s.sample_id,
IF(s.result = f.criteria, -1, /* unspecified behavior */
IF(s.result > f.criteria, 1,
IF(sample_nb_chemicals = total_nb_chemicals, 2, 3))) AS grp
FROM
tbl_fac f JOIN tbl_samp s
ON f.chemical_id = s.chemical_id
JOIN (
SELECT s.sample_id,
COUNT(DISTINCT f.chemical_id) AS sample_nb_chemicals
FROM tbl_fac f JOIN tbl_samp s
ON f.chemical_id = s.chemical_id
GROUP BY s.sample_id
) u
ON s.sample_id = u.sample_id
CROSS JOIN (
SELECT COUNT(DISTINCT chemical_id) AS total_nb_chemicals
FROM tbl_fac
) v
GROUP BY sample_id, grp
Related
Hi all I am having 3 tables as follows
Technology_table
Technology_ID Technology_Name
10 Asp.Net
20 C#
Question_table
QUESTION_ID Technology_ID QUESTION_DESCRIPTION
1 10 First ques in Asp.net
2 20 First ques in C#
Reply_table
QUESTION_ID Technology_ID Reply_Date Reply_Message
1 10 2016-01-23 I am first to post
1 10 2016-01-24 I am second to post
I have written the following query but not getting the result as expected
select
FI.QUESTION_ID,FI.QUESTION_TITLE,FI.USER_NAME,FI.DATE_POSTED,
FI.[DATE_REPLIED],FI.RepliedName,FI.VIEW_COUNT,FI.REPLY_COUNT,
FI.REPLY_MESSAGE,TT.TECHNOLOGY_ID,TT.TECHNOLOGY_NAME
from FORUM_TECHNOLOGY TT,
( select distinct
TQ.TECHNOLOGY_ID,TQ.QUESTION_ID,TQ.QUESTION_TITLE,TQ.USER_NAME,
TQ.DATE_POSTED,
TR.[DATE_REPLIED],
TR.USER_NAME as RepliedName,
TQ.VIEW_COUNT,TQ.REPLY_COUNT,TR.REPLY_MESSAGE
from FORUM_QUESTIONS TQ
LEFT OUTER JOIN FORUM_REPLIES TR
ON TR.TECHNOLOGY_ID=TQ.TECHNOLOGY_ID
and TR.QUESTION_ID = TQ.QUESTION_ID
and TR.[DATE_REPLIED] in (
select MAX(TR.[DATE_REPLIED])
from FORUM_REPLIES TR
group by TR.QUESTION_ID
)
) FI
where FI.TECHNOLOGY_ID =TT.TECHNOLOGY_ID
and TT.TECHNOLOGY_ID = #TechID
I also tried in this way
select t1.QUESTION_ID,oa.USER_NAME,oa.REPLY_MESSAGE
from FORUM_QUESTIONS t1
cross apply(select top 1 * from FORUM_REPLIES
where QUESTION_ID = t1.QUESTION_ID
order by DATE_REPLIED desc)oa
join FORUM_TECHNOLOGY t2 on oa.TECHNOLOGY_ID = t2.TECHNOLOGY_ID
AND oa.QUESTION_ID = t1.QUESTION_ID
I would like to display only one instead of duplicates
Can some one help me
This assumes that your DATE_REPLIED has a valid time component and isn't just omitted or defaulted to midnight.
SELECT FQ.QUESTION_ID
, FQ.QUESTION_TITLE
, FQ.USER_NAME
, FQ.DATE_POSTED
, FR.DATE_REPLIED
, FR.RepliedName
, FQ.VIEW_COUNT
, FI.REPLY_COUNT
, FR.REPLY_MESSAGE
, TT.TECHNOLOGY_ID
, TT.TECHNOLOGY_NAME
FROM FORUM_TECHNOLOGY AS TT
INNER JOIN FORUM_QUESTION AS FQ
ON FQ.TECHNOLOGY_ID = TT.TECHNOLOGY_ID
LEFT OUTER JOIN (
SELECT QUESTION_ID
, COUNT(*) AS REPLY_COUNT
, MAX(DATE_REPLIED) AS DATE_REPLIED
FROM FORUM_REPLIES
GROUP BY QUESTION_ID
) AS FI
ON FI.QUESTION_ID = FQ.QUESTION_ID
INNER JOIN FORUM_REPLIES AS FR
ON FR.QUESTION_ID = FI.QUESTION_ID
AND FR.DATE_REPLIED = FI.DATE_REPLIED
Could you please help me to make this query works
SELECT *
FROM `SC_orders`
LEFT JOIN `SC_customer_reg_fields_values` using(customerID)
WHERE (`statusID` = 2 OR `statusID` = 3 OR `statusID` = 21 OR `statusID` = 25 OR `statusID` = 26) AND DATE(order_time) > '2012-12-01 00-00-00'
LEFT JOIN `SC_ordered_carts`
ON orderID = orderID
GROUP BY orderID
I try to combine information from 3 tables in one output. This query works fine without last LEFT JOIN and Grouping. Where is my mistake?
The where needs to be after the last join. also, the second ON clause is ambiguous and I think the group by is unnecessary since you don't have any aggregate functions:
SELECT *
FROM `SC_orders`
LEFT JOIN `SC_customer_reg_fields_values` using(customerID)
LEFT JOIN `SC_ordered_carts` using(orderID)
WHERE (`statusID` = 2 OR `statusID` = 3 OR `statusID` = 21 OR `statusID` = 25 OR `statusID` = 26) AND DATE(order_time) > '2012-12-01 00-00-00'
I have multiple table for a project (sessions , charges and payments)
To get the sessions i'm doing the following :
SELECT
sess.file_id, SUM(sess.rate * sess.length) AS total
FROM
sess
WHERE sess.sessionDone = 1
GROUP BY sess.file_id
This will return the amount that a specific student should pay
I also have another table "charges"
SELECT
file_charges.file_id, SUM(file_charges.price) AS total_charges
FROM
file_charges
GROUP BY file_charges.file_id
And finally the payment query :
SELECT
file_payments.file_id, SUM(file_payments.paymentAmount) AS total_payment
FROM
file_payments
GROUP BY file_payments.file_id
Can i combine those 3 in a way to have :
Total = Payments - (Session + Charges)
Note that it could be negative so i could have file_id that exists in session , charges but not in payments and i could have a payment without sessions or charges ...
Edit : http://sqlfiddle.com/#!2/a90d9
One issue that needs to be addressed is whether one of these queries can be the "driver", in cases where we don't have rows for a given file_id returned by one or more of the queries. (e.g. there might be rows from sess, but none from file_payments. If we want to be sure to include every possible file_id that appears in any of the queries, we can get a list of all possible file_id with a query like this:
SELECT ss.file_id FROM sess ss
UNION
SELECT fc.file_id FROM file_charges fc
UNION
SELECT fp.file_id FROM file_payments fp
(NOTE: The UNION operator will remove any duplicates)
To get the specified resultset, we can use that query, along with "left joins" of the other three original queries. The outline of the query will be:
SELECT a.file_id, p.total_payment - ( s.total + c.total_charges)
FROM a
LEFT JOIN s ON s.file_id = a.file_id
LEFT JOIN c ON c.file_id = a.file_id
LEFT JOIN p ON p.file_id = a.file_id
ORDER BY a.file_id
In that statement a is a standin for the query that gets the set of all file_id values (as shown above). The s, c and p are standins for your three original queries, on sess, file_charges and file_payments, respectively.
If any of the file_id values is "missing" from any of the queries, we are going to need to substitute a zero for the missing value. We can use the IFNULL function to handle that for us.
This query should return the specified resultset:
SELECT a.file_id
, IFNULL(p.total_payment,0) - ( IFNULL(s.total,0) + IFNULL(c.total_charges,0)) AS t
FROM ( -- all possible values of file_id
SELECT ss.file_id FROM sess ss
UNION
SELECT fc.file_id FROM file_charges fc
UNION
SELECT fp.file_id FROM file_payments fp
) a
LEFT
JOIN ( -- the amount that a specific student should pay
SELECT sess.file_id, SUM(sess.rate * sess.length) AS total
FROM sess
WHERE sess.sessionDone = 1
GROUP BY sess.file_id
) s
ON s.file_id = a.file_id
LEFT
JOIN ( -- charges
SELECT file_charges.file_id, SUM(file_charges.price) AS total_charges
FROM file_charges
GROUP BY file_charges.file_id
) c
ON c.file_id = a.file_id
LEFT
JOIN ( -- payments
SELECT file_payments.file_id, SUM(file_payments.paymentAmount) AS total_payment
FROM file_payments
GROUP BY file_payments.file_id
) p
ON p.file_id = a.file_id
ORDER BY a.file_id
(The EXPLAIN for this query is not going to be pretty, with four derived tables. On really large sets, performance may be horrendous. But the resultset returned should meet the specification.)
Beware of queries that JOIN all three tables together... that will likely give incorrect results when there are (for example) two (or more) rows for the same file_id in the file_payment table.
There are other approaches to getting an equivalent result set, but the query above answers the question: "how can i get the results of these queries joined together into a total".
Using correlated subqueries
Here's another approach, using correlated subqueries in the SELECT list...
SELECT a.file_id
, IFNULL( ( SELECT SUM(file_payments.paymentAmount) FROM file_payments
WHERE file_payments.file_id = a.file_id )
,0)
- ( IFNULL( ( SELECT SUM(sess.rate * sess.length) FROM sess
WHERE sess.file_id = a.file_id )
,0)
+ IFNULL( ( SELECT SUM(file_charges.price) FROM file_charges
WHERE file_charges.file_id = a.file_id )
,0)
) AS tot
FROM ( -- all file_id values
SELECT ss.file_id FROM sess ss
UNION
SELECT fc.file_id FROM file_charges fc
UNION
SELECT fp.file_id FROM file_payments fp
) a
ORDER BY a.file_id
try this
SELECT sess.file_id, SUM(file_payments.paymentAmount) - (SUM(sess.rate * sess.length)+SUM(file_charges.price)) as total_payment FROM sess , file_charges , file_payments
WHERE sess.sessionDone = 1
GROUP BY total_payment
EDIT.
SELECT a.file_id
, IFNULL(p.total_payment,0) - ( IFNULL(s.total,0) + IFNULL(c.total_charges,0)) AS tot
FROM (
SELECT ss.file_id FROM sess ss
UNION
SELECT fc.file_id FROM file_charges fc
UNION
SELECT fp.file_id FROM file_payments fp
) a
LEFT JOIN (
SELECT sess.file_id, SUM(sess.rate * sess.length) AS total
FROM sess
WHERE sess.sessionDone = 1
GROUP BY sess.file_id
) s
ON s.file_id = a.file_id
LEFT JOIN (
SELECT file_charges.file_id, SUM(file_charges.price) AS total_charges
FROM file_charges
GROUP BY file_charges.file_id
) c
ON c.file_id = a.file_id
LEFT JOIN (
SELECT file_payments.file_id, SUM(file_payments.paymentAmount) AS total_payment
FROM file_payments
GROUP BY file_payments.file_id
) p
ON p.file_id = a.file_id
ORDER BY a.file_id
DEMO HERE
When I run this procedure:
SELECT * FROM photo
LEFT JOIN photo_selectedTags
ON photo.COUNTER = photo_selectedTags.PHOTO_COUNTER
WHERE photo_selectedTags.TAGS_COUNTER IN (6,192)
It retrieves rows where TAGS_COUNTER = 6 OR 192
How do I retrieve the rows from 'photo' where TAGS_COUNTER = 6 AND 192?
Corrected: the IN for ALL
The basic idea is this:
SELECT PHOTO_COUNTER
FROM photo_selectedTags
WHERE TAGS_COUNTER in (6, 192)
group by PHOTO_COUNTER
having count(distinct TAGS_COUNTER) = 2 --2 matches # of items in IN clause
You can then do this to get the rest of the columns:
SELECT *
from PHOTO_COUNTER
LEFT JOIN photo_selectedTags
ON photo.COUNTER = photo_selectedTags.PHOTO_COUNTER
where photo.COUNTER in (
SELECT PHOTO_COUNTER
FROM photo_selectedTags
WHERE TAGS_COUNTER in (6,192)
group by PHOTO_COUNTER
having count(distinct TAGS_COUNTER) = 2 --2 matches # of items in IN clause
) a
Edit
Now that i understand what you want and DB structure, try with this:
SELECT * FROM photo
LEFT JOIN photo_selectedTags
ON photo.COUNTER = photo_selectedTags.PHOTO_COUNTER
WHERE photo_selectedTags.TAGS_COUNTER = 6 AND photo_id IN
(SELECT photo_id FROM photoSELECT * FROM photo
LEFT JOIN photo_selectedTags
ON photo.COUNTER = photo_selectedTags.PHOTO_COUNTER
WHERE photo_selectedTags.TAGS_COUNTER = 192)
I don't know if photo_id is an actual field of your table, but try to adapt it to your structure
Obviously in the first SELECT don't insert PHOTO_COUNTER because i'll be always the same value and it haven't much sense.
I would propose to use 2 joins
SELECT *
FROM photo
JOIN photo_selectedTags as photo_selectedTags6 -- this join restricts to 'photo.COUNTER' whic have TAGS_COUNTER = 6
ON photo.COUNTER = photo_selectedTags6.PHOTO_COUNTER
AND photo_selectedTags6.TAGS_COUNTER = 6
JOIN photo_selectedTags as photo_selectedTags192 -- this join restricts to 'photo.COUNTER' whic have TAGS_COUNTER = 192
ON photo.COUNTER = photo_selectedTags192.PHOTO_COUNTER
AND photo_selectedTags192.TAGS_COUNTER = 192
Also would be possible to achive it with analytical functions (if supported by your DB)
-- This one works on teradata. Something similar should work on oracle. Don't know about others
SELECT *
FROM photo
LEFT JOIN photo_selectedTags
ON photo.COUNTER = photo_selectedTags.PHOTO_COUNTER
QUALIFY max(case when photo_selectedTags.TAGS_COUNTER = 6 then 1 end) over (partition by photo.COUNTER) = 1
AND max(case when photo_selectedTags.TAGS_COUNTER = 192 then 1 end) over (partition by photo.COUNTER) = 1
If you have many values in the list (in addition to 192,6), then this might be possible solution
SELECT *
FROM photo
JOIN
(
SELECT PHOTO_COUNTER, count(distinct TAGS_COUNTER) cnt
FROM photo_selectedTags
WHERE TAGS_COUNTER in (192,6)
HAVING cnt = 2 -- adjust this according to the number of different values
) as pht
ON photo.COUNTER = pht.PHOTO_COUNTER
In subquery only PHOTO_COUNTERs are left which have both (192 and 6), then this is joined
I have a SQL statement in which I do this
... group by date having date between '2010-07-01' and '2010-07-10';
The result looks like:
sum(test) day
--------------------
20 2010-07-03
120 2010-07-07
33 2010-07-09
42 2010-07-10
So I have these results, but is it possible, that I can write a statement that returns me for every day in the "between" condition a result row in this kind:
sum(test) day
--------------------
0 2010-07-01
0 2010-07-02
20 2010-07-03
0 2010-07-04
0 2010-07-05
0 2010-07-06
120 2010-07-07
... ...
42 2010-07-10
Otherwise, if this is not possible, I have to do it in my program logic.
Thanks a lot in advance & Best Regards.
Update: Perhaps it will be better if I will show you the full SQL statement:
select COALESCE(sum(DUR), 0) AS "r", 0 AS "opt", DATE_FORMAT(date, '%d.%m.%Y') AS "day" from (
select a.id as ID, a.dur as DUR, DATE(FROM_UNIXTIME(REVTSTMP / 1000)) as date,
a_au.re as RE, a_au.stat as STAT from b_c
join c on b_c.c_id = c.id
join a on c.id = a.c_id
join a_au on a.id = a_au.id
join revi on a_au.re = revi.re
join (
select a.id as ID, DATE(FROM_UNIXTIME(REVTSTMP / 1000)) as date,
max(a_au.re) as MAX_RE from b_c
join c on b_c.c_id = c.id
join a on c.id = a.c_id
join a_au on a.id = a_au.id
join revi on a_au.re = revi.re
where b_c.b_id = 30 group by ID, date) x on
x.id = a.id and x.date = date and x.MAX_RE = a_au.rev
where a_au.stat != 7
group by ID, x.date)
AS SubSelTable where date between '2010-07-01' and '2010-07-15' group by date;
Update:
My new SQL statement (-> Dave Rix):
select coalesce(`theData`.`real`, 0) as 'real', 0 as 'opt', DATE_FORMAT(`DT`.`ddDate`, '%d.%m.%Y') as 'date'
from `dimdates` as DT
left join (
select coalesce(sum(DUR), 0) AS 'real', 0 AS 'opt', date
from (
select a.id as ID, a.dur as DUR, DATE(FROM_UNIXTIME(REVTSTMP / 1000)) as date, a_au.RE as RE, a_au.stat as STAT
from b_c
join c on b_c.c_id = c.id
join a on c.id = a.c_id
join a_au on a.id = a_au.id
join revi on a_au.RE = revi.RE
join (
select a.id as ID, DATE(FROM_UNIXTIME(REVTSTMP / 1000)) as date, max(a_au.RE) as MAX_RE
from b_c
join c on b_c.c_id = c.id
join a on c.id = a.c_id
join a_au on a.id = a_au.id
join revi on a_au.RE = revi.RE
where b_c.b_id = 30 GROUP BY ID, date
) x
on x.id = a.id and x.date = date and x.MAX_RE = a_au.RE
where a_au.stat != 20
group by ID, x.date
) AS SubTable
where date between '2010-07-01' and '2010-07-10' group by date) AS theData
ON `DT`.`ddDate` = `theData`.`date` where `DT`.`ddDate` between '2010-07-01' and '2010-07-15';
Put the Between Logic in a Where Clause
Select Sum(day), day
From Table
Where day Between date1 and date2
Group By day
EDIT:
Having should only be used to filter data in the aggregates... i.e.
Having Sum(day) > 10
Check out my answer to the following question;
Select all months within given date span, including the ones with 0 values
This may be just what you are looking for :)
You can modify your query above as follows (you could integrate this, but this way is simpler!);
SELECT COALESCE(`theData`.`opt`, 0), `DT`.`myDate`
FROM `dateTable` AS DT
LEFT JOIN (
... INSERT YOUR QUERY HERE ...
) AS theData
ON `DT`.`myDate` = `theData`.`date`
and you will also need to change the DATE_FORMAT(date, '%d.%m.%Y') AS "day" in your query to just date
E.g.
select COALESCE(sum(DUR), 0) AS "r", 0 AS "opt", `date` from
As for #OMG Ponies answer, you will need to pre-populate the dateTable with plenty of rows of data!
Does anyone know how I can post my SQL dump of this table as a file which can be attached? It's quite big, but can be useful...
Assuming that your date column is a DATETIME column, you need to use something to change time values to be the same for proper grouping to happen. IE:
SELECT SUM(t.test),
DATE_FORMAT(t.date, '%Y-%m-%d') AS day
FROM TABLE t
WHERE t.date BETWEEN #start AND #end
GROUP BY DATE_FORMAT(t.date, '%Y-%m-%d')
But if there's no record for a given date, the date will not appear in the result set. In other words, no dates with zero will appear in your output.
To solve that, you need to LEFT JOIN to a table of dates, which MySQL doesn't have the ability to generate. It can't even generate a list of numbers, so you have to create a table with a single column:
DROP TABLE IF EXISTS `example`.`numbers`;
CREATE TABLE `example`.`numbers` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
...and populate it:
INSERT INTO numbers (id) VALUES (NULL)
...before you can use the number value to generate a list of dates using the DATE_ADD function:
SELECT COALESCE(SUM(t.test), 0),
x.the_date AS day
FROM (SELECT DATE_FORMAT(DATE_ADD(NOW(), INTERVAL n.id-1 DAY), '%Y-%m-%d') AS the_date
FROM NUMBERS n) x
LEFT JOIN your_table yt ON DATE_FORMAT(yt.date, '%Y-%m-%d') = x.the_date
WHERE x.the_date BETWEEN #start AND #end
GROUP BY x.the_date