Literate SQL
modern-sql.com
modern-sql.com
The main database was IBM DB2 9.7 on RHEL and it had about 3 months (800GB) of data in it (we pruned it nightly). We had a "reporting" database that was postgres 9.3 or 9.4 (can't remember). It was the same schema but was never pruned. It had about 15 billion rows total and about 5TB of data. About 700GB was indexes. The same query when ran on DB2 took 12 hours, on postgres (5x more data) it took 24 hours.
The query plans were mostly identical (neither one did much to optimize the WITH statements). The speed difference seemed to come from Postgres just accessing the disk better.
When doing an index scan, the disk read speed was about 80MBs, while DB2 was 10MBs. Full table scans in Postgres were between 180-220MBs while DB2 was around 60MBs. Same storage too (netapp over nfs). DB2 was using direct IO so it like to read in 4k chunks, but even disabling that didn't help much.
I've been recently running TPC-DS on PostgreSQL 9.5 (alpha1 IIRC), to see how we're doing (I'm one of the contributors). Thanks to the grouping sets, we're now able to execute all 99 queries in the benchmark, which is awesome. Regarding performance, I've expected grouping sets to be a performance issue as the current implementation is rather simple. But to my mild surprise that's not the case - CTEs are the main problem.
On 100GB data set some queries took hours to complete (well, I interrupted them so I'm not sure how long it'd actually take). After a rewrite (replace CTE with a subquery), the query took a a minute or so.
[0]: https://wiki.postgresql.org/wiki/Todo#Optimizer_.2F_Executor. Search for CTE.
Consider for example this:
WITH x AS (SELECT a, b FROM t) SELECT * FROM x x1 JOIN x x2 ON (x1.a = x2.b);
Now, had this been evaluated using a merge join, the "x" CTE would have to be sorted first by "a" or "b" (for either side of the join). Well, that can't really happen.
Another issue is locking - consider this version, for example:
WITH x AS (SELECT a, b FROM t FOR UPDATE) SELECT * FROM x x1 JOIN x x2 ON (x1.a = x2.b);
In other words, it's way more complicated than it might seem, especially if you can't break existing uses (e.g. the locking).
Not disputing what you're saying, but I swear I read something the other day recommending being careful using CTE's as they are re-executed once for every reference in the query (or something to that effect).
So the statement that "a CTE is only evaluated once" should not be part of any specification for the language. And I would not expect two different executions of a query to maintain that invariant if the optimizer finds it better not to.
The problem is we currently have an implementation that behaves as explained above (planned in isolation, evaluated once), and there are applications relying on that behaviour. We can't just change that without breaking them.
I'm not arguing for just abruptly reverse the design, but that the aim should be to reverse in an orderly manner with suitable deprecations and migration paths in place.
But don't forget: CTEs can be recursive.
Big no-no right there -- a reporting database shouldn't have the same schema as your main transactional DB, because the latter is designed and built for write-heavy workloads, not reporting or analytical ones.
I'm pretty certain that a lot of the stuff happening in the CTEs could simply be designed as part of a "true" reporting schema and ETL'ed in as a batch job.
That being said, my focus in my company is more on the front end. In this case there's a range of acceptable timeframes, but we've had clients with a strict requirement that any report with exposure to VPs or higher has to have <=2 second response time. Even outside of that organization, more than a minute for a report is usually pretty much a non-starter.
The problems I'm referring to have nothing to do with a BI guy implementing complicated logic; this is just a part of the job description. Let me quote the section where it becomes clear that there's a huge problem.
> The main database was IBM DB2 9.7 on RHEL and it had about 3 months (800GB) of data in it (we pruned it nightly). We had a "reporting" database that was postgres 9.3 or 9.4 (can't remember). It was the same schema but was never pruned. It had about 15 billion rows total and about 5TB of data. About 700GB was indexes. The same query when ran on DB2 took 12 hours, on postgres (5x more data) it took 24 hours.
Emphasis is mine. They're reporting on an OLTP schema. The reason that the query is many pages long, and that it takes 24 hours to run, is because the CTEs are being used as in-line ETL. Their "reporting" database is nothing of the sort, it's just a replica of their operational system.
That poor BI guy is either out of his depth and doesn't know how or is simply not allowed to build the appropriate solution. Both are problems, and both extend far beyond the BI guy.
Edit: Extra word word.
The java software devs, even though they all had 15+ years experience, really had no clue what a database was other than a thing to put data in. App environment configs, customer configs, binary images,... you name it and it was stuck in the main database. The schema was designed by a sysadmin that left shortly after the company (a start up) actually started getting real customers. Messing with the schema was basically off limits. No one saw an issue with it.
Everyone thought the schema was brilliant because that sysadmin thought it would be a cool idea to replace indexes with partitions (DB2 9.7 at the time had a brand new "partition by range" feature). He wrote giant bash scripts to partition many tables by timestamps on either a 15 minute, one hour, or one day interval AND by groups of account IDs (or any other ID that might be in the row). His logic was that even though all queries will do a full table scan.. but hey these are all really tiny tables now! Management and the senior devs thought it was a brilliant idea. It didn't work so well in the end, and ultimately indexes were just added randomly all over the place. But the partitions remained and the scripts would run every hour... but sometimes they'd fail to add partitions because of locks. Which ultimately left no where for certain data to go (based on timestamps) and the app would go down. This went on for years...
But the other issue, as time rolled on, when the schema did need to be changed, it was done the quickest and dirtiest way possible. One patch I saw go through added a "username" column to several tables to track what user made a change to a row, but it was varchar instead of a FK... completely de-normalizing the schema. No one knew or understood the data on the dev team.
I tried best as I could to do analytics, but the DB2 DBA's were always on my case for "accessing" "their" database. I managed to get buy in from my manager to start standing up postgres databases for reporting. We tried Mysql at first but its supported SQL syntax was too butchered for anything other than simple queries.
The idea was to have a carbon copy of the OLTP schema in postgres and populate it with Pentaho or some other BI tools/scripts. Once that was mostly working we were going to build a data warehouse schema with stuff in the proper formats so we could just query without having to transform. Sadly after a few months my manager moved me off of the project and put a rather new contractor over the project who knew nothing about data architecture, or linux (he constantly complained about the command line and would say things like "I stopped using DOS years ago"), or even postgres. I was moved to simply help with some vmware upgrades which was boring. They then opened a "jr DBA" position and filled that with someone straight out of college to handle standing up the postgres instances.
And with that, I left.... and eventually most of the rest of the team too.
PS, they are looking for a BI guy...
You were definitely on the right track to set up an ODS and then stand up a DW in front of that. Looks like you were firmly stuck in the "not allowed" camp and trying to work around absurd limitations.
> PS, they are looking for a BI guy...
No thanks.
I'm spoiled by being a consultant. Anyone who wants to pay our rates is already committed to making changes.
You're doing it wrong. These type of queries should be run on a column store (e.g., Vertica).
OLTP and OLAP are hugely different workloads. A good 3NF (or close to it) schema that you'll find in an OLTP database is designed for fast writes with minimal locking; it fits well with the workload of OLTP.
If you're reporting on that data and performing analytical queries, then your schema needs to reflect that and optimize for large reads without much concern around lock contention for the primary workload.
I am not opposing the suggestion of a columnstore database, as we utilize these extensively with our clients. There is simply a much larger problem in the specified example. 15B rows is not too big a deal in an RDBMS for an analytical workload.
SELECT
Foo,
Sum(Baz) AS Bar
FROM Quux
Use instead: SELECT
Foo,
Bar = Sum(Baz)
FROM Quux
The advantage here is that the names also line up neatly and the shape (column names etc.) of the result is obvious.It seems to me that sticking everything in CTEs would be equally as jarring. Writing SQL or reading and understanding someone else's SQL is almost always a non-linear process, not least of all because the processing order by the database engine isn't top-to-bottom.