using an alias of a function in a sql condition - sql

I have something like this:
SELECT
cansa1.NAME,
mod(cansa1.PRODUCT_ID, 1000000) prodIdHash
FROM CANSA_TABLE cansa1
INNER JOIN CUSER_TABLE cuser1 ON cansa1.PRODUCT_ID = cuser1.PRODUCT_ID
AND mod(cansa1.PRODUCT_ID, 1000000) = cuser1.PRODUCT_HASH
This query is working, but I want replace the second occurrence (in the inner join) of the mod() function, to avoid execute it two times. I tried replace it by the alias in the select clause but not works. Any idea of that I can use to make this query don't repeat the mod() function?
Sorry by my english

Don't worry about executing it twice, the SQL engine will optimize the query and will decide whether the function value is cached or it executes twice and can end up re-writing the query so that what is executed has a different structure than the written query because it has determined that it would be more efficient.
If you really want to try to rewrite it then:
SELECT c.NAME,
c.prodIdHash
FROM (
SELECT name,
mod(PRODUCT_ID, 1000000) As prodIdHash
FROM CANSA_TABLE
) c
INNER JOIN CUSER_TABLE u
ON ( c.PRODUCT_ID = u.PRODUCT_ID
AND c.prodIdHash = u.PRODUCT_HASH )
However, the SQL engine may rewrite the query and push the function to the outer scope so you may need a seemingly irrelevant filter condition to materialize the inner query and force the calculation not to be rewritten:
SELECT c.NAME,
c.prodIdHash
FROM (
SELECT name,
mod(PRODUCT_ID, 1000000) As prodIdHash
FROM CANSA_TABLE
WHERE ROWNUM > 0
) c
INNER JOIN CUSER_TABLE u
ON ( c.PRODUCT_ID = u.PRODUCT_ID
AND c.prodIdHash = u.PRODUCT_HASH )
However, this really seems like a case of premature optimisation. You should check if there is actually a problem first before you try and apply an optimisation that probably is not needed.

You can use a derived table (i.e. a subquery in the FROM clause):
SELECT dt.NAME, dt.prodIdHash
FROM
(SELECT
cansa1.NAME,
mod(cansa1.PRODUCT_ID, 1000000) prodIdHash
FROM CANSA_TABLE cansa1) dt
INNER JOIN CUSER_TABLE cuser1 ON dt.PRODUCT_ID = cuser1.PRODUCT_ID
AND dt.prodIdHash = cuser1.PRODUCT_HASH

Related

Selecting ambiguous column from subquery with postgres join inside

I have the following query:
select x.id0
from (
select *
from sessions
inner join clicked_products on sessions.id0 = clicked_products.session_id0
) x;
Since id0 is in both sessions and clicked_products, I get the expected error:
column reference "id0" is ambiguous
However, to fix this problem in the past I simply needed to specify a table. In this situation, I tried:
select sessions.id0
from (
select *
from sessions
inner join clicked_products on sessions.id0 = clicked_products.session_id0
) x;
However, this results in the following error:
missing FROM-clause entry for table "sessions"
How do I return just the id0 column from the above query?
Note: I realize I can trivially solve the problem by getting rid of the subquery all together:
select sessions.id0
from sessions
inner join clicked_products on sessions.id0 = clicked_products.session_id0;
However, I need to do further aggregations and so do need to keep the subquery syntax.
The only way you can do that is by using aliases for the columns returned from the subquery so that the names are no longer ambiguous.
Qualifying the column with the table name does not work, because sessions is not visible at that point (only x is).
True, this way you cannot use SELECT *, but you shouldn't do that anyway. For a reason why, your query is a wonderful example:
Imagine that you have a query like yours that works, and then somebody adds a new column with the same name as a column in the other table. Then your query suddenly and mysteriously breaks.
Avoid SELECT *. It is ok for ad-hoc queries, but not in code.
select x.id from
(select sessions.id0 as id, clicked_products.* from sessions
inner join
clicked_products on
sessions.id0 = clicked_products.session_id0 ) x;
However, you have to specify other columns from the table sessions since you cannot use SELECT *
I assume:
select x.id from (select sessions.id0 id
from sessions
inner join clicked_products
on sessions.id0 = clicked_products.session_id0 ) x;
should work.
Other option is to use Common Table Expression which are more readable and easier to test.
But still need alias or selecting unique column names.
In general selecting everything with * is not a good idea -- reading all columns is waste of IO.

where clause in nested isNull query giving unexpected result

The below query returns around 200000 results.
The working of nested where clause in this query is not very clear i.e where is it coming in the picture ?
If I comment out the where clause inside the isNull then I get 0 results, which is fine and expected as the Max(invoiceID) is not null after join.
select * from CustomerServices where isNull((
SELECT MAX(invoiceid)
FROM Invoices
LEFT JOIN InvoicesHistory
ON InvoicesHistory.ServiceHistoryID = Invoices.ServiceHistoryID
WHERE serviceID = Invoices.serviceID
),0)=0
Please let me know if you want me to add more information.
I think you just want not exists:
select cs.*
from CustomerServices cs
where not exists (select 1
from Invoices i left join
InvoicesHistory ih
on ih.ServiceHistoryID = i.ServiceHistoryID
where cs.serviceID = i.serviceID
);
In your case, the nested WHERE clause is not doing anything. It is equivalent to:
Invoices.serviceID = Invoices.serviceID
by the scoping rules in SQL. In all likelihood, this is intended to be a correlation clause and hence needs a qualified column name.

SQL subquery multiple times error

I am making a subquery but I am getting a strange error
The column 'RealEstateID' was specified multiple times for 'NotSold'.
here is my code
SELECT *
FROM
(SELECT *
FROM RealEstatesInfo AS REI
LEFT JOIN Purchases AS P
ON P.RealEstateID=REI.RealEstateID
WHERE DateBought IS NULL) AS NotSold
INNER JOIN OwnerEstate AS OE
ON OE.RealEstateID=NotSold.RealEstateID
It's on SQL server by the way.
That's because there will be 2 realestiteids in your subquery. You need to change it to explicitly list the columns from both table and only include 1 realestateid. It doesn't matter which as you use it for your join.
If you're very Lazy you can select rei.* and only name the p cols apart from realestateid.
Btw select * is probably never a good idea in sub queries or derived tables or ctes.

error occuring :ORA-00907: missing right parenthesis . not able execute query

select s.mname,count(distinct s.actorlist) as act
from p s
group by s.mname
having count(distinct s.actorlist) =
(select max(t.act)
from (
select s1.mname,count(distinct s1.actorlist) as act
from p s1
group by s1.mname
) as t );
error occuring :ORA-00907: missing right parenthesis . not able execute query
Can someone please help me in finding out the error.
Alas, Oracle does not permit as for a table alias:
select s.mname,count(distinct s.actorlist) as act
from p s
group by s.mname
having count(distinct s.actorlist) =
(select max(t.act)
from (select s1.mname, count(distinct s1.actorlist) as act
from p s1
group by s1.mname
) t
);
Do note, however: I would recommend using window functions for this purpose, rather than nested subqueries.
That would look like:
select s.*
from (select s.mname, count(distinct s.actorlist) as act,
max(count(distinct s.actorlist)) over () as max_act
from p s
group by s.mname
) s
where act = max_act;
The subquery approach can be simplified as follows:
select mname, count(distinct actorlist) as act
from p
group by mname
having count(distinct actorlist) = ( select max( count(distinct actorlist) )
from p
group by mname
);
Notice how the subquery (in the HAVING condition) works: it groups the rows in table p by mname, then it computes distinct actorlist within each group, and then it takes the MAX over all groups (over all mnames).
I don't see the benefit of using aliases here, either for tables or for columns. I also see no point in aliasing a table called p to s - even if you need to fully qualify column names, p.actorlist is perfectly fine. By NOT using aliases, you make it clear to a future developer that the subquery is self-contained (it is not correlated to anything in the outer query - it simply computes a number, in a self-contained manner, and it returns that number to the HAVING clause).
In this case, I am not convinced that analytic functions are needed. (Of course, the GROUP BY solution no longer has different levels of nested subqueries, so the reason to consider analytic functions is no longer there.)
You could avoid reading the base table twice by using dense_rank last but I would only look at that if performance is poor; otherwise this solution looks pretty clean and easy to explain to anyone and to maintain.

SQL WITH clause doesn't work

I'm trying to execute seemingly simple request contains WITH clause:
WITH sub AS (SELECT url FROM site WHERE id = 15)
SELECT * FROM search_result WHERE url = sub.url
But it doesn't work. I get
ERROR: missing FROM-clause entry for table "sub"
What's the matter?
Table expressions need to be used like tables. You're trying to use the value of sub as a scalar.
Try this (forgive me, Postgres is not my first SQL dialect).
WITH sub AS (SELECT url FROM site WHERE id = 15)
SELECT * FROM sub
INNER JOIN
search_result
ON
sub.url = search_result.url
EDIT, alternatively, you could just skip the WITH clause and go with:-
SELECT * FROM
site
INNER JOIN
search_result
ON
site.url = search_result.url
WHERE
site.id = 15
Don't use a CTE at all for this simple case.
Unlike you seem to be expecting, the following simple query without a CTE will be slightly faster:
SELECT r.*
FROM search_result r
JOIN site s USING (url)
WHERE s.id = 15;
Test with EXPLAIN ANALYZE to verify.
CTEs introduce an optimization barrier. They have many very good uses, but they won't make simple queries faster.
Here is a thread on pgsql-performance that gives you more details as to why that is.
That's not the correct way to use a CTE:
With sub as (
SELECT url
FROM site
WHERE id = 15
)
SELECT *
FROM Search_Result SR
JOIN sub ON SR.url = sub.Url
You can just as easily do an inner join:
SELECT search_result .*
FROM
search_result
INNER JOIN
(SELECT url FROM site WHERE id = 15) as st
ON
search_result.url = st.url
This does the filtering so that you are joining on a smaller set than if you did the where clause outside of the filtering. This may not matter in your case, but it is something to consider.