Show HN: Better SQL group by date
ankane.github.io
ankane.github.io
The project is pretty cool, but I don't think it's worth adding the dependency lock-in for the functions.
Simply, if you know ahead of time that you want GROUP BY date, then you should create a new column for each interval. `week`, `day`, `hour`.
That way you will have fast queries...
create index foo1 on t1 (date(timestamp))
create index foo2 on t2 (extract(week from timestamp))
Postgres can use almost any row expression as an index. group by date(col)
or group by date(col), hour(col), floor(minute(col)/5)
an index on col is enough, or at least the last time I checked for my specific queries it was. If you need something like group by hour-of-day you'll need to create an indexed column of course. (this is with mariadb but I think basic mysql handles it too)Mysql will still use the column for a covering index and run the query in a single-pass, but it will still generate a temporary table for the results (and do a really slow sort unless you do 'order by null'). It doesn't look like mysql has any way of doing date grouping without temporaries even in the trivial cases. I guess eliminating the sort was enough for my queries.
Using date()-like functions, date_format(), and left() all have identical query plans and roughly comparable performance.
EDIT: Technically chaz is right and a data warehouse puts all these date columns in a separate table you can join against.
Beware, however, that, while there are certain cases that mysql can utilize indexes in a group by clause if values are constant [1], mysql definitely won't be able to use indexes with any of these functions (I'm not very familiar with postgres so somebody else would have to comment on that). If your load is relatively light or your table is small enough you'd probably be fine, otherwise I would either 1) set another column to the value you want when inserting the row or 2) have a cron run in the background to fill in values on another column, something like update tbl set creation_week = gd_week(creation_timestamp) where creation_week is null.
[1] http://dev.mysql.com/doc/refman/5.0/en/group-by-optimization...
If the functions are marked stable or immutable, then the function bodies will be inlined directly into the query.
Example use case: http://www.techtalkz.com/microsoft-sql-server/170861-groupin...
Convert the date to seconds since date X (or unix timestamp / 60) and divide by the number of mins you need and convert to int.
Use the INTERVAL data type, it handles the logic properly.
http://www.postgresql.org/docs/9.2/static/xfunc-volatility.h...
This allows the planner to optimize queries that use the functions correctly, instead of treating the functions like a black box.
Edit: STABLE would indeed be better, forgot about timezones.
psql=$ \df+ date_trunc
Schema | Name | Result data type | Argument data types | Type | Volatility | Owner | Language | Source code | Description
------------+------------+-----------------------------+-----------------------------------+--------+------------+----------+----------+-------------------+------------------------------------------------------
pg_catalog | date_trunc | interval | text, interval | normal | immutable | postgres | internal | interval_trunc | truncate interval to specified units
pg_catalog | date_trunc | timestamp without time zone | text, timestamp without time zone | normal | immutable | postgres | internal | timestamp_trunc | truncate timestamp to specified units
pg_catalog | date_trunc | timestamp with time zone | text, timestamp with time zone | normal | stable | postgres | internal | timestamptz_trunc | truncate timestamp with time zone to specified unitsFor example:
CREATE OR REPLACE FUNCTION gd_day(timestamptz, text)
RETURNS timestamptz AS
$$
SELECT DATE_TRUNC('day', $1 AT TIME ZONE $2) AT TIME ZONE $2;
$$
LANGUAGE SQL;
EDIT: The reason that some time functions in PostgreSQL are not immutable is that they are affected by the current time zone setting of the session.This seems to add unnecessary complexity and overhead.
There is already plenty of build-in abstraction for date manipulation in MySQL, and you really don't want to be dependent on any stored procedure or function if you can avoid it.
http://dev.mysql.com/doc/refman/5.5/en/date-and-time-functio...
I can't speak for postgres though. It might be different.
For an example see: http://explainextended.com/2009/08/11/efficient-date-range-q...
You can easily extend this map into more dimensions depending in your common use cases and the capability of your DB's spatial library.
Thanks for sharing!
Also, you don't have to use the root user - I'll make that more clear. Thanks for the feedback!
how do you know the script is the same on subsequent requests?
Figuring out how to solve the TOCTOU problem for a small script in a source control repo is should not be difficult for anyone actually qualified to tell if a script is evil or not by looking at it.
Maybe the use case is for installable packages that would be used with different backend databases... but in general, it seems like a bad idea.
DATE_TRUNC('day', $1 AT TIME ZONE $2) AT TIME ZONE $2;
or
CONVERT_TZ(DATE_FORMAT(CONVERT_TZ(ts, '+00:00', time_zone), '%Y-%m-%d 00:00:00'), time_zone, '+00:00');
are not very intuitive (or aren't to me at least).
DAYOFYEAR( CONVERT_TZ(my_datetime_field,'+00:00', '+02:00') )
Seems very intuitive to me.
I've recently looked at a project where there were a number of postgres functions floating around and it seemed terribly difficult to set breakpoints, profile, and troubleshoot.
Maybe I'm missing something?
Don't forget while DB scaling can be an issue the overwhelming majority of projects have a single DB machine that sit's mostly idle. (EX: slashdot can run just fine with a single DB even if they keep a hot spare running.)
Data logic, however, is worth thinking through. When you have a lot of calculations that may span multiple tables, or need fine-grained access control, or require multiple reusable result sets that would benefit from being indexed and delivered to a user (versus attempts to map all this through ORM objects or pulling out raw data and then transforming it in app code), or require querying and packaging the same datasets (or combinations thereof) to share them with other systems that access via ODBC, utilizing functions, stored procedures, and database views are there for a reason.
That some ORMs make it difficult to leverage this functionality does not speak to the lack of need for separate data logic and taking advantage of database capabilities. If all you're building is CRUD apps with a pretty face, then you likely don't need it. If, however, you're building pretty complicated systems, pulling out more raw data than you need to calculate a result that could be calculated and kept up-to-date in a database is far easier and reusable. Moreover, ORMs often sour in the face of very complicated data requirements that are rather trivial to setup in a view, function, or stored procedure.
As far as onboarding is concerned, in my humble opinion, if you are bringing in new talent to deal with data who do not have a strong understanding of SQL, I'd be pretty wary of far more significant problems developing as a result of not being familiar with what the ORM is doing behind the scenes (and of what performance tradeoffs are occurring).
As far as troubleshooting is concerned, if you can confirm that the data is leaving the database as expected by data logic/code--and this is as simple as executing a query, function, sproc, or view--then you can at least limit yourself to discovering where the offending business logic is located.
Because that is where they belong. A database is not a storage device, it is a application development environment. Everything about your data should be in your database, including the rules, restrictions, relationships, and ways of manipulating it. In this case, trying to do it in a layer above the database would mean either doing it horribly inefficiently by copying too much data to the app server and then grouping it, or embedding the SQL into your app which means you need to go and find it and copy+paste it when you want to do that manually at any time for testing/debugging/etc. Plus, when you end up with multiple applications using a database, do you really want to be keeping multiple copies of the same code in sync across multiple apps for no reason?
(edit: spelling)
(Now we just need one for Oracle, DB2, and Sybase...)
Oracle, MySQL, PostgreSQL, sqlite, MS-SQL and Access.
Six intotal.