Ask HN: Have you ever used PL/pgSQL – the PostgreSQL programming language?
If you have, what was your experience using it? Was it positive or negative? When do you think it is suitable to use it?
Here is an overview of the language:
https://www.postgresql.org/docs/10/static/plpgsql-overview.html
35 comments
[ 4.6 ms ] story [ 83.9 ms ] threadThe exception to this of course being unless your application is already on your database.
https://github.com/openstreetmap/Nominatim/blob/master/sql/f...
List of small https://github.com/openstreetmap/Nominatim/issues?q=is%3Aiss... and larger projects https://wiki.openstreetmap.org/wiki/Google_Summer_of_Code/20...
I think it is pretty simple to use, but I've also had to fix a lot of poorly-written PL/pgSQL. There is a LOT of nuances to the language that can blow your feet off, and the language doesn't really scale out to multiple people working on it. You just have to find one person who is good. You also need to have an editor with snippets because the language is very verbose.
Personally, I'd rather use PL/pgSQL than any off-the-shelf ORM, but as I said, it wouldn't scale to more employees very well. The language solves one of the main use-cases of ORMs via the use of entities.
https://www.postgresql.org/docs/10/static/plpgsql-statements...
PL/pgSQL gives you %I and %L, which allows you to specify table and column names. Basically, if you want to `select * from %I`, you have the option to call the specific table.
In my opinion, try and use as much SQL as you can first. Functions are often an optimization barrier for the postgresql query planner. The postgresql implementation of SQL has a bunch of neat functionality in it that replaces things that often happen in apps, like window functions, and the docs are so great.
They are hard to debug. Try to keep their functionality small.
My experience, overall, is positive. Consider using it if the amount of data transferred to the app and evaluated there is getting absurd. But try to creatively solve the problem with pure SQL first before moving to functions.
In postgres, CTEs are optimization barriers, that is, the implementation plans and executes the CTE separately and the main query consumes the result of the CTE. Pure SQL functions can sometimes be inlined, that is, made part of the containing query, but functions in the other languages cannot. In particular, if a function runs queries itself those happen in a new execution context (portal) and don't share anything with the function callers plan.
VOLATILE/STABLE/IMMUTABLE is definitely a choice worth considering and making in an educated way when writing a function.
Also, using WITH to create CTEs makes SQL easy to read, but sometimes will slow down a query considerably. I have a feeling that new releases will make attempts to mitigate this issue in some cases.
It took a week to code up, another 2-3 for expressing the initial huge BP and a random BP test driver. Worked great.
My only question is whether I would do it again the same way. I liked that there was not some external system that had to be running for the BP to run, but it is not hard to arrange that and then one gets a much more accessible programming environemnt (such as Lisp). I guess I will not know until I get to do it again.
Now that doesn't mean PL/PGSQL, because I like to use the simplest tool that does the job. From simplest to most complex, you can:
1. Put it in the table definition, things like: data type, null or not null, unique, foreign key, and check constraints:
2. Put it in a database view definition. If your SQL is getting long and complex, save it to a view, so your application doesn't have a tower of SQL in its source code. Views can also warn you when you're changing tables in a way that will break something ("You can't drop this column because this view depends on it, you can't change this column's type to that because a view depends on it, etc."). Of course you can drop and recreate the view after changing the table, but at least it warns you. Other times, like if you rename a column, I think the view updates automatically. None of this would happen automatically if the SQL is hardcoded in your Python script.3. Put it in an SQL function (SQL, not PL/PGSQL).
4. Put it in a PL/PGSQL function.
That's the order I take, from smallest hammer to largest.
What is your observation on the performance of such a system, when compared to systems where the middle language has all the logic and the database is used just as a dumb datastore?
Also, what are your observations on the scalability of such a system you have mentioned above?
Performance is usually better in the database, because the data is local. However, like any language, there is more than one way to say it in SQL. When the query is complicated, with joins and subselects and aggregates, I have made it literally a thousand times faster by rearranging and rewriting things. I'm not even talking about adding indexes yet.
> scalability
I think 99% of applications could scale just fine with Postgres.
And unlike a lot of ETL and reporting tools it doesn’t involve a lot of pointing and clicking. You can put PL/pgSQL in version control along with your R and Python code. That’s very handy for repeatability.
My company likes using it simply because it's great for that heavy duty data manipulation.
For data it's great however my current site uses it for many other things such as heavy xml tranformations, web service calls etc and then the gaps in the language start to show through and you've be better off moving into something general purpose, but you can do almost anything with it if you want...
I would say my experience was pretty positive, I would consider doing something like this again for the right use case. It saved a lot of back-and-forth queries between the app and database and allowed the logic in the app to be much more simple than it otherwise would have been, but at the cost of insert throughput into the triggered tables.
If you are at all considering this route, I would recommend the excellent project, pgtap, for testing: https://pgtap.org/
I used pgtap, along with the cli utility, pgprove, to run tests.