On 2016/05/18 12:26 AM, Jonathan wrote: > Hi Ryan, > Thanks for the excellent and comprehensive answer; it seems like CTE's > are the way to go for this. > I did a quick google and this tutorial was very helpful (for anyone > else newly interested in CTE's - > https://blog.expensify.com/2015/09/25/the-simplest-sqlite-common-table-expression-tutorial/ > > ) - they're a lot simpler than I was expecting based on the code > snippets I've posted here over time. I think I can even get my head > around them, though it may take a while to fully grok your response!
I will add a detailed description of what my response CTE does below this mail, but any questions you may have regarding CTE's or any other SQL functionality, please feel free to ask - we like answering. > > Incidentally, you mentioned that this solution didn't have the "fewest > iterations" - can I trouble to ask what that would look like? Then I > can try working backwards and seeing what the differences are. Actually, I wrote that while I had another layout, and then decided to put in the more complicated but less iterative version (but did not amend my statement), so the one I've sent is the already trimmed down version - apologies. > > (Note: having my data type of "DATE" - it worked just fine as you > suspected it would.) > > Many thanks again, > Jonathan It's our pleasure. --------------------------------------------------------------- CTE 101: WITH dtRange(minDT,maxDT) AS ( SELECT MIN(time_date), '2016-01-15 00:00:00' FROM my_table -- This first CTE table sets up a single line with 2 columns that is essentially just values -- we will use later, a kind of SQL version of initializing variables. -- The variables being initialized here are minDT which is where the calendar will start, and -- maxDT, which is where it will end. I use the MIN() aggregate from my_table for the prior, -- and for the latter simply added a date by hand namely 15 January 2016. You could specify -- any dates you like for both. ), dtAll(dt) AS ( SELECT minDT FROM dtRange UNION ALL SELECT datetime(dt,'+1 day') FROM dtAll,dtRange WHERE dt < maxDT -- This second CTE basically creates a list of all the dates we are interested in (whether -- they exist in my_table or not). It starts by selecting the minDT from our previously made -- dtRange CTE, and then adds 1 day repeatedly in the recursive part of this recursive CTE -- until we reach the maxDT (again, from the joined dtRange table). ), my_full(time_date, num) AS ( SELECT time_date, num FROM my_table UNION ALL SELECT DT, 0 FROM dtAll -- This CTE does the magic. We simply add the dates from the original my_table along with -- their "num" values first. We then list all the made-up dates from the longer date list -- CTE (dtAll) using the UNION, but with Zero values for "num" (so that it doesn't register -- in the SUM() aggregate later). -- NOTE: we could be more efficient here in data-size terms by only adding dates that -- doesn't already exist in my_table, but in practice the time taken for the lookup dwarfs -- the time added by the aggregate calcs. ) SELECT date(time_date) as time_date, sum(num) AS sum_num FROM my_full GROUP BY time_date ORDER BY time_date ASC -- Finally, in the actual SELECT part, we refer the my_full CTE and SUM() the num values -- which shows all the aggregates for all dates - which happen to be Zero for all our -- "made-up" dates, but have values for all dates found in the my_table table. -- It's just like any other programming language with an elaborate for-loop construct. :) ; Hope it helped. If you happen to be on Windows you could find the SQLitespeed DB manager from http://www.rifin.co.za/software/sqlc/download/ and install it with ticking the option to add the example scripts. In there are quite a few CTE examples and tutorials (stored to your Documents\SQLiteScripts\ folder I think).