PostgreSQL query I don't understand - postgresql

I'm sorry for my title, but it's totally right. I don't know PostgreSQL well, but I have to take over other person's application. I know SQL, so it's usually no problem to take over any application based on MSSQL, Oracle, MySQL, PostgreSQL, ... but here is some kind of PostgreSQL facet. Could anyone, please, explain this query to me?
select company_generate_course_template_fc
((select company_id from company order by 1 desc limit 1)::int)

The query calls the function company_generate_course_template_fc() passing the result of the query: select company_id from company order by 1 desc limit 1 as an argument. The result is cast to an integer using ::int (see the manual for details)
Apart from the ::int part (and the different ways of limiting the result to a single row), this wouldn't be much different in other databases
The ANSI SQL equivalent of ::int would be cast(... as integer)

If you are talking about ::int it's type casting from string (company_id) to integer value

Related

SQL query to calculate first and second order

I have a table with order_id, Customer_Id and Order_date. I can calculate first time order, as it is an easy 'min' on the date, but for some reason I can't find a way to get the second order date per customer in SQL.
Order_id|Customer_Id|Order_date
Ideal result:
Customer_Id|First_order_date|Second_order_date
I am using Postgres and have beginners level of SQL.
Happy to get any suggestions.
Many thanks for your help,
Misael
What I have tried
Tried using 'row_number() over(partition by customer_id', but I can't seem to make it work. I believe I need to declare what a second purchase means and use a common table expression.

PostgreSQL order by TSRange

I think the question is pretty self explanatory, I've been learning about Postgres TSRange column type, but I've not been able to find an example of how you can order by it... How do you order by a TSRange, or specifically the lower bound?
There are range functions described in documentation.
SELECT * FROM my_table ORDER BY lower(range_column);

Can PostgreSQL recommend optimal field types?

MySQL offers PROCEDURE ANALYSE. Given a query like
SELECT `field` FROM `table` PROCEDURE ANALYSE();
The result offers a suggested field type
Optimal_fieldtype: TINYINT(2) UNSIGNED NOT NULL
Does PostgreSQL offer a similar functionality? I've looked at pg_statistic/pg_stats. Seems like I can use this information to infer what data type might be appropriate, but it would be handy if Postgres could recommend an actual data type itself.
There is no equivalent in Postgres for MySQL's PROCEDURE ANALYSE that I know of. (And I think I would know.)
For the given example, obviously about an integer-type column, I would:
SELECT min(field), max(field) FROM tbl;
And check against the range of possible numeric data types in the manual.
Or for the distribution of distinct values:
SELECT field, count(*) AS ct FROM tbl GROUP BY 1 ORDER BY 2 DESC;
Actually, any DBA with a minimum of experience should know the essential characteristics of basic data types. The best type for each use case depends on a lot more than the current range and distribution of values. MySQL's PROCEDURE ANALYSE() would basically be assistance to newcomers - who would easily misinterpret results.

Dynamic FROM clause in Postgres

Using PostgreSQL 9.1.13 I've written the followed query to calculate some data:
WITH windowed AS (
SELECT a.person_id, a.category_id,
CAST(dense_rank() OVER w AS float) / COUNT(*) OVER (ORDER BY category_id) * 100.0 AS percentile
FROM (
SELECT DISTINCT ON (person_id, category_id) *
FROM performances s
-- Want to insert a FROM clause here
INNER JOIN person p ON s.person_id = p.ident
ORDER BY person_id, category_id, created DESC
) a
WINDOW w AS (PARTITION BY category_id ORDER BY score)
)
SELECT category_id,percentile FROM windowed
WHERE person_id = 1;
I now want to turn this into a stored procedure but my issue is that in the middle there, where I showed the comment, I need to place a dynamic WHERE clause. For example, I'd like to add something like:
WHERE p.weight > 110 OR p.weight IS NULL
The calling application let's people pick filters and so I want to be able to pass the appropriate filters into the query. There could be 0 or many filters, depending on the caller, but I could pass it all in as a properly formatted where clause as a string parameter, for example.
The calling application just sends values to a webservice, which then builds the string and calls the stored procedure, so SQL injection attacks won't really be an issue.
The calling application just sends values to a webservice, which then
builds the string and calls the stored procedure, so SQL injection
attacks won't really be an issue.
Too many cooks spoil the broth.
Either let your webserive build the SQL statement or let Postgres do it. Don't use both on the same query. That leaves two possible weak spots for SQL injection attacks and makes debugging and maintenance a lot harder.
Here is full code example for a plpgsql function that builds and executes an SQL statement dynamically while making SQL injection impossible (just from two days ago):
Robust approach for building SQL queries programmatically
Details heavily depend on exact requirements.

PostgreSQL -must appear in the GROUP BY clause or be used in an aggregate function

I am getting this error in the pg production mode, but its working fine in sqlite3 development mode.
ActiveRecord::StatementInvalid in ManagementController#index
PG::Error: ERROR: column "estates.id" must appear in the GROUP BY clause or be used in an aggregate function
LINE 1: SELECT "estates".* FROM "estates" WHERE "estates"."Mgmt" = ...
^
: SELECT "estates".* FROM "estates" WHERE "estates"."Mgmt" = 'Mazzey' GROUP BY user_id
#myestate = Estate.where(:Mgmt => current_user.Company).group(:user_id).all
If user_id is the PRIMARY KEY then you need to upgrade PostgreSQL; newer versions will correctly handle grouping by the primary key.
If user_id is neither unique nor the primary key for the 'estates' relation in question, then this query doesn't make much sense, since PostgreSQL has no way to know which value to return for each column of estates where multiple rows share the same user_id. You must use an aggregate function that expresses what you want, like min, max, avg, string_agg, array_agg, etc or add the column(s) of interest to the GROUP BY.
Alternately you can rephrase the query to use DISTINCT ON and an ORDER BY if you really do want to pick a somewhat arbitrary row, though I really doubt it's possible to express that via ActiveRecord.
Some databases - including SQLite and MySQL - will just pick an arbitrary row. This is considered incorrect and unsafe by the PostgreSQL team, so PostgreSQL follows the SQL standard and considers such queries to be errors.
If you have:
col1 col2
fred 42
bob 9
fred 44
fred 99
and you do:
SELECT col1, col2 FROM mytable GROUP BY col1;
then it's obvious that you should get the row:
bob 9
but what about the result for fred? There is no single correct answer to pick, so the database will refuse to execute such unsafe queries. If you wanted the greatest col2 for any col1 you'd use the max aggregate:
SELECT col1, max(col2) AS max_col2 FROM mytable GROUP BY col1;
I recently moved from MySQL to PostgreSQL and encountered the same issue. Just for reference, the best approach I've found is to use DISTINCT ON as suggested in this SO answer:
Elegant PostgreSQL Group by for Ruby on Rails / ActiveRecord
This will let you get one record for each unique value in your chosen column that matches the other query conditions:
MyModel.where(:some_col => value).select("DISTINCT ON (unique_col) *")
I prefer DISTINCT ON because I can still get all the other column values in the row. DISTINCT alone will only return the value of that specific column.
After often receiving the error myself I realised that Rails (I am using rails 4) automatically adds an 'order by id' at the end of your grouping query. This often results in the error above. So make sure you append your own .order(:group_by_column) at the end of your Rails query. Hence you will have something like this:
#problems = Problem.select('problems.username, sum(problems.weight) as weight_sum').group('problems.username').order('problems.username')
#myestate1 = Estate.where(:Mgmt => current_user.Company)
#myestate = #myestate1.select("DISTINCT(user_id)")
this is what I did.