Postgresql, how to query the database based on a certain condition? - postgresql

I was trying to write a postgresql statement that would allow me to execute a certain query only if a condition is satisfied:
select SUM("TotalBookings") AS Availability,
from (select count (*) as "TotalBookings"
from bookings
where hotel_id=2 and ('2023-02-10') between symmetric check_in_date and check_out_date
union all
select -count (*) as "TotalRooms"
from hotels
inner join rooms on rooms.hotel_id = hotels.hotel_id
where hotels.hotel_id=2 ) Availability;
case
when Availability >= 1
then select *
from hotels
where hotel_id=2
end
I basically would like to show my hotels only when the sum is greater than one.
Below the error returned:
syntax error at or near "from"
Any ideas on whether mine is only a syntax error or if my logic is wrong as well ?
Thank you in advance.

Related

Full outer join with different WHERE clauses in Knex.js for PostgreSQL

I try to get a single row with two columns showing aggregation results: one column should show the total sum based on one WHERE-clause while the other column should show the total sum based on a different WHERE clause.
Desired output:
amount_vic amount_qld
100 70
In raw PostgreSQL I could write something like that:
select
sum(a.amount) as amount_vic,
sum(b.amount) as amount_qld
from mytable a
full outer join mytable b on 1=1
where a.state='vic' and b.state= 'qld'
Question: How do I write this or a similar query that returns the desired outcome in knex.js? For example: the 'on 1=1' probably needs knex.raw() and I think the table and column aliases do not work for me and it always returns some errors.
One of my not-working-attempts in knex.js:
knex
.sum({ amount_vic: 'a.amount' })
.sum({ amount_qld: 'b.amount' })
.from('mytable')
.as('a')
.raw('full outer join mytable on 1=1')
.as('b')
.where({
a.state: 'vic',
b.state: 'qld'
})
Thank you for your help.
Disclaimer: this does not answer the Knex part of the question - but it is too long for a comment.
Although your current query does what you want, the way it is phrased seems suboptimal. There is not need to generate a self-cartesian product here - which is what full join ... on 1 = 1 does. You can just use conditional aggregation.
In Postgres, you would phrase this as:
select
sum(amount) filter(where state = 'vic') amount_vic,
sum(amount) filter(where state = 'qld') amount_qld
from mytable
where state in ('vic', 'qld')
I don't know Knex so I cannot tell how to translate the query to it. Maybe this query is easier for you to translate.

Eloquent Order By and Group By

I've been searching for 2 hours on how to build a query with eloquent using groupBy and orderBY.
This is my situation: I have a table orders with these columns:
idOfOrder, quantityOfProduct, userWhoBuy, productBuy, statusOfOrder
I would like to get the 3 most sold products. For this, I need to build a query who group all data from ProductBy, sum theme from quantity and sort theme by desc. But it's terribly difficult.
This my actual query:
$buyObj->select('product',DB::raw('count(*) as total'))
->where('status','=','1')
->groupBy('product')
->orderBy('quantity','desc')
->take(3)
->get();
I get this error :
SQLSTATE[42000]: Syntax error or access violation: 1055 Expression #1 of ORDER BY clause is not in GROUP BY clause and contains nonaggregated column 'web_projet_exia.buys.quantity' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by (SQL: select product, count(*) as total from buys where status = 1 group by product order by quantity desc limit 3) "
Do you know how to fix that ?
Thanks in advance !
The solution is just to order by total and not by quantity. Thanks to #JonasStaudenmeir

Distinct Join to find data that does NOT match - Teradata

really struggling with this... I have written the following code that seems to work and identifies the row ID of 40,000 addresses that match where FrontDoorColour is RED.
SELECT DISTINCT ID
FROM Database.table1
WHERE table1.address = table2.address
AND table1.FrontDoorColour = 'RED'
The problem I have is when I want to reverse this and identify the 10,000 addresses where FrontDoorColour is RED but where the address does NOT match.
I run the same query but swap
WHERE table1.address = table2.address
for
WHERE table1.address <> table2.addres
Instead of generating the 10,000 NON-matching rows, I get a spool space error (2646)
Any suggestions would be greatly appreciated!
Thanks
An EXPLAIN output of the second query should yield PRODUCT JOIN and is likely the reason for the spool error you received. The first query may also yield a product join but it may process within your spool allocation. The following SQL should help you find address ids from Table1 where the address is not found in Table2 and the door in Table1 is RED for the address id.
SELECT DISTINCT t1.id
FROM Database.Table1 t1
WHERE NOT EXISTS (SELECT 1
FROM Database.Table2 t2
WHERE t1.address = t2.address)
AND t1.FrontDoorColour = 'RED';

Postgres undefined column when using CASE

Using this SQL, I can cast a boolean column to a text:
SELECT *, (CASE WHEN bars.some_cond THEN 'Yes' ELSE 'No' END) AS some_cond_alpha
FROM "foos"
INNER JOIN "bars" ON "bars"."id" = "foos"."bar_id";
So why do I get a PG::UndefinedColumn: ERROR: column "some_cond_alpha" does not exist when I try to use it in a WHERE clause?
SELECT *, (CASE WHEN bars.some_cond THEN 'Yes' ELSE 'No' END) AS some_cond_alpha
FROM "foos"
INNER JOIN "bars" ON "bars"."id" = "foos"."bar_id"
WHERE (some_cond_alpha ILIKE '%y%');
This is because the column is created on-the-fly and does not exist. Possibly in later editions of PG it will, but right now you can not refer to an alias'd column in the WHERE clause, although for some reason you can refer to the alias'd column in the GROUP BY clause (don't ask me why they more friendly in the GROUP BY)
To get around this, I would make the query into a subquery and then query the column OUTSIDE the subquery as follows:
select *
from (
SELECT *, (CASE WHEN bars.some_cond THEN 'Yes' ELSE 'No' END) AS some_cond_alpha
FROM "foos"
INNER JOIN "bars" ON "bars"."id" = "foos"."bar_id"
) x
WHERE (x.some_cond_alpha ILIKE '%y%')
NOTE: It is possible at some point in the future you will be able to refer to an alias'd column in the WHERE clause. In prior versions, you could not refer to the alias in the GROUP BY clause but since 9.4 + it is possible...
SQL evaluates queries in a rather counterintuitive way. It starts with the FROM and WHERE clauses, and only hits the SELECT towards the end. So aliases defined in the SELECT don't exist yet when we're in the WHERE. You need to do a subquery if you want to have access to an alias, as shown in Walker Farrow's answer.
When I read an SQL query, I try to do so in roughly this order:
Start at the FROM. You can generally read one table/view/subquery at a time from left to right (or top to bottom, depending on how the code is laid out); it's normally not permissible for one item to refer to something that hasn't been mentioned yet.
Go down, clause by clause, in the order they're written. Again, read from left to right, top to bottom; nothing should reference anything that hasn't been defined yet. Stop right before you hit ORDER BY or something which can only go after ORDER BY (if there is no ORDER BY/etc., stop at the end).
Jump up to the SELECT and read it.
Go back down to where you were and resume reading.
If at any point you see a subquery, apply this algorithm recursively.
If the query begins with WITH RECURSIVE, go read the Postgres docs for 20 minutes and figure it out.

HAVING clause in PostgreSQL

I'm rewriting the MySQL queries to PostgreSQL. I have table with articles and another table with categories. I need to select all categories, which has at least 1 article:
SELECT c.*,(
SELECT COUNT(*)
FROM articles a
WHERE a."active"=TRUE AND a."category_id"=c."id") "count_articles"
FROM articles_categories c
HAVING (
SELECT COUNT(*)
FROM articles a
WHERE a."active"=TRUE AND a."category_id"=c."id" ) > 0
I don't know why, but this query is causing an error:
ERROR: column "c.id" must appear in the GROUP BY clause or be used in an aggregate function at character 8
The HAVING clause is a bit tricky to understand. I'm not sure about how MySQL interprets it. But the Postgres documentation can be found here:
http://www.postgresql.org/docs/9.0/static/sql-select.html#SQL-HAVING
It essentially says:
The presence of HAVING turns a query
into a grouped query even if there is
no GROUP BY clause. This is the same
as what happens when the query
contains aggregate functions but no
GROUP BY clause. All the selected rows
are considered to form a single group,
and the SELECT list and HAVING clause
can only reference table columns from
within aggregate functions. Such a
query will emit a single row if the
HAVING condition is true, zero rows if
it is not true.
The same is also explained in this blog post, which shows how HAVING without GROUP BY implicitly implies a SQL:1999 standard "grand total", i.e. a GROUP BY ( ) clause (which isn't supported in PostgreSQL)
Since you don't seem to want a single row, the HAVING clause might not be the best choice.
Considering your actual query and your requirement, just rewrite the whole thing and JOIN articles_categories to articles:
SELECT DISTINCT c.*
FROM articles_categories c
JOIN articles a
ON a.active = TRUE
AND a.category_id = c.id
alternative:
SELECT *
FROM articles_categories c
WHERE EXISTS (SELECT 1
FROM articles a
WHERE a.active = TRUE
AND a.category_id = c.id)
SELECT * FROM categories c
WHERE
EXISTS (SELECT 1 FROM article a WHERE c.id = a.category_id);
should be fine... perhaps simpler ;)