How can you add 2 joins in a subquery? - sql

I am trying to get information from 3 tables in my database. I am trying to get 4 fields. 'kioskid', 'kioskhours', 'videotime', 'sessiontime'. In order to do this, i am trying a join in a subquery. This is what I have so far:
SELECT k.kioskid, k.hours, v.time, s.time
FROM `nsixty_kiosks` as k
LEFT JOIN (SELECT time
FROM `nsixty_videos`
ORDER BY videoid) as v
ON kioskid = k.kioskid LEFT JOIN
(SELECT kioskid, time
FROM `sessions`
ORDER BY pingid desc LIMIT 1) as s ON s.kioskid = k.kioskid
WHERE hours is NOT NULL
When I run this query, it works but it shows every row instead of just showing the last row of each kiosk id. Which is meant to show based on the line 'ORDER BY pingid desc LIMIT 1'.
Any body have some ideas?

Instead of joining to s, you can use a correlated subquery:
SELECT k.kioskid,
k.hours,
v.time,
( SELECT time
FROM sessions
WHERE sessions.kioskid = k.kioskid
ORDER
BY pingid DESC
LIMIT 1
)
FROM nsixty_kiosks AS k
LEFT
JOIN ( SELECT time
FROM `nsixty_videos`
ORDER BY videoid
) AS v
ON kioskid = k.kioskid
WHERE hours IS NOT NULL
;
N.B. I didn't fix your LEFT JOIN (...) AS v, because I don't understand what it's trying to do, but it too is broken; the ON clause doesn't refer to any of its columns, and there's no point in having an ORDER BY in a subquery unless you also have a LIMIT or whatnot in there.

Well, your join on the 'v' subquery doesn't actually reference the 'v' subquery, nor does the 'v' subquery even contain a kioskid field to JOIN on, so that's undoubtedly part of the problem.
To go much further we'd need to see schema and sample data.

Related

Limit number of rows fetching in a left outer join in Oracle

I'm going to create a data model in oracle fusion applications. I need to create column End_date based on two tables in the query. So I used two methods.
Using a subquery:
SELECT *
FROM (SELECT projects_A.end_date
FROM projects_A, projects_B
WHERE projects_A.p_id = projects_B.p_id
AND rownum = 1)
Using a LEFT OUTER JOIN:
SELECT projects_A.end_date
FROM projects_A
LEFT JOIN projects_B
ON projects_A.p_id = projects_B.p_id
WHERE rownum = 1
Here when I used a subquery, the query returns the results as expected. But when I use left outer join with WHERE rownum = 1 the result is zero. Without WHERE rownum = 1 it retrieves all the results. But I want only the first result. So how can I do that using left outer join? Thank you.
Looks like you want to bring a non-null end_date value(So, add NULLS LAST), but the sorting order is not determined yet(you might add a DESC to the end of the ORDER BY clause depending on this fact ), and use FETCH clause(the DB version is 12c+ as understood from the comment) with ONLY option to exclude ties as yo want to bring only single row.
So, you can use the following query :
SELECT A.end_date
FROM projects_A A
LEFT JOIN projects_B B
ON A.p_id = B.p_id
ORDER BY end_date NULLS LAST
FETCH FIRST 1 ROW ONLY

Trying to update column values in SQL Server based on time of insertion and getting "The column "ID" was specificed multiple times for "p""

Here is my query that's throwing the "The column "ID" was specified multiple times for "p"":
update tracking.tag set
tracking.tag.PageViewID = p.id
, tracking.tag.BrowserInfoID = p.BrowserInfoID
from (
select
t.id, t.[name], t.VisitID, t.CreatedDate, p.id, p.VisitID, p.BrowserInfoID
from [Tracking].[Tag] as t
inner join (
select id, visitid, BrowserInfoID, createddate, uri
from [tracking].[PageView]
) as p on abs(datediff(second, p.CreatedDate, t.createddate)) < 1 and p.VisitID = t.VisitID
order by 1 desc
) as p
I've seen quite a few questions with the same error on SO but can't seem to see what to apply in this scenario. Any help is greatly appreciated.
Unfortunately there is a lot broken with your statement. The error you are getting is the least of your worries and it in fact just a typo. Let me go through them.
The error: if you consider the following query, which is in essence what you have, how does SQL Server know which of the 2 columns in your sub-query to refer to? They are both called id! Hence if you need to select both columns you need to alias one of them to a unique name.
select id
from (
select
t.id, p.id
from [Tracking].[Tag] as t
inner join [tracking].[PageView] as p
on ABS(datediff(second, p.CreatedDate, t.createddate)) < 1
and p.VisitID = t.VisitID
) as p
Fixed:
select id -- Now we have a unique id column, so SQL Server knows which to select.
from (
select
t.id TagID, p.id
from [Tracking].[Tag] as t
inner join [tracking].[PageView] as p
on ABS(datediff(second, p.CreatedDate, t.createddate)) < 1
and p.VisitID = t.VisitID
) as p
You have a syntax error with your ORDER BY, you can't order a sub-query in that way as it doesn't mean anything.
This is a recommendation, but don't reuse the same table alias (in your case P) in multiple nested sub-queries because its really confusing to know which table/derived table you are referencing.
Your inner-most sub-query is un-necessary, just join the table directly.
Finally you aren't actually joining the table you are updating onto the query you are producing, yes you do have a join inside, but thats not the same table reference as the one you are updating. I assume thats why you have attempted to add an ORDER BY inside your sub-query despite the fact that its giving you a syntax error. In fact all you need is a simple UPDATE + JOIN as follows:
-- Note you use the table alias here for the update rather than the table name
update t set
PageViewID = p.id
, BrowserInfoID = p.BrowserInfoID
-- I assume this select is what you were running into issues with as you tried to test that your update was correct.
-- In this format you no longer need to alias the duplicate column names, but you could for clarity
-- select t.id TagID, t.[name], t.VisitID TagVisitId, t.CreatedDate, p.id, p.VisitID, p.BrowserInfoID
from [Tracking].[Tag] as t
inner join [tracking].[PageView] as p on abs(datediff(second, p.CreatedDate, t.createddate)) < 1 and p.VisitID = t.VisitID

Postgres left outer join appears to not be using table indices

Let me know if this should be posted on DBA.stackexchange.com instead...
I have the following query:
SELECT DISTINCT "court_cases".*
FROM "court_cases"
LEFT OUTER JOIN service_of_processes
ON service_of_processes.court_case_id = court_cases.id
LEFT OUTER JOIN jobs
ON jobs.service_of_process_id = service_of_processes.id
WHERE
(jobs.account_id = 250093
OR court_cases.account_id = 250093)
ORDER BY
court_cases.court_date DESC NULLS LAST,
court_cases.id DESC
LIMIT 30
OFFSET 0;
But it takes a good 2-4 seconds to run, and in a web application this is unacceptable for a single query.
I ran EXPLAIN (ANALYZE, BUFFERS) on the query as suggested on the PostgreSQL wiki, and have put the results here: http://explain.depesz.com/s/Yn6
The table definitions for those tables involved in the query is here (including the indexes on foreign key relationships):
http://sqlfiddle.com/#!15/114c6
Is it having issues using the indexes because the WHERE clause is querying from two different tables? What kind of index or change to the query can I make to make this run faster?
These are the current sizes of the tables in question:
PSQL=# select count(*) from service_of_processes;
count
--------
103787
(1 row)
PSQL=# select count(*) from jobs;
count
--------
108995
(1 row)
PSQL=# select count(*) from court_cases;
count
-------
84410
(1 row)
EDIT: I'm on Postgresql 9.3.1, if that matters.
or clauses can make optimizing a query difficult. One idea is to split the two parts of the query into two separate subqueries. This actually simplifies one of them a lot (the one on court_cases.account_id).
Try this version:
(SELECT cc.*
FROM "court_cases" cc
WHERE cc.account_id = 250093
ORDER BY cc.court_date DESC NULLS LAST,
cc.id DESC
LIMIT 30
) UNION ALL
(SELECT cc.*
FROM "court_cases" cc LEFT OUTER JOIN
service_of_processes sop
ON sop.court_case_id = cc.id LEFT OUTER JOIN
jobs j
ON j.service_of_process_id = sop.id
WHERE (j.account_id = 250093 AND cc.account_id <> 250093)
ORDER BY cc.court_date DESC NULLS LAST, id DESC
LIMIT 30
)
ORDER BY court_date DESC NULLS LAST,
id DESC
LIMIT 30 OFFSET 0;
And add the following indexes:
create index court_cases_accountid_courtdate_id on court_cases(account_id, court_date, id);
create index jobs_accountid_sop on jobs(account_id, service_of_process_id);
Note that the second query uses and cc.count_id <> 250093, which prevents duplicate records. This eliminates the need for distinct or for union (without union all).
I'll try modifying the query as the following:
SELECT DISTINCT "court_cases".*
FROM "court_cases"
LEFT OUTER JOIN service_of_processes
ON service_of_processes.court_case_id = court_cases.id
LEFT OUTER JOIN jobs
ON jobs.service_of_process_id = service_of_processes.id and jobs.account_id = 250093
WHERE
(court_cases.account_id = 250093)
ORDER BY
court_cases.court_date DESC NULLS LAST,
court_cases.id DESC
LIMIT 30
OFFSET 0;
I think that the issue is in the fact that the where filter is not properly decomposed by query planner optimizer, a really strange performance bug

order by count when you have to count only certain items

I have this query:
SELECT tips.*
FROM `tips` `t`
LEFT JOIN tip_usage
ON tip_usage.tip_id = t.id
GROUP BY t.id
ORDER BY COUNT(CASE
WHEN status = 'Active' THEN status
ELSE NULL
END) DESC
As you see here, I use a left join and I count only the records which are Active
Can I make this query to be different, and to lose the case stuff from the count?
If you want to return all tips, regardless of status, and but sort by number of Active records, then this is as pretty as you are going to get.
If you only want to return active tips, then you can add Where status = 'Active' and then just order by Count(t.id) desc.
One alternative is that you have a NumActive int column in the tips table, and you keep this update whenever a new tip_usage record is added or modified for a given tip. This puts more overhead into the insert/delete/update operations for tip_usage, but would make this query much simpler:
select *
from tips
Order by tips.NumActive desc
Another alternative is:
Select tips.*
From tips
Order by (
Select count(tip_id)
From tips_usage as t
Where t.tip_id = tips.id and status = 'Active') DESC
Though this exchanges a case for a subquery, so just complex in a different way.
Quick note, you cannot select t.* and group on t.id. So with that being said:
SELECT t.id,coalesce(tu.cntUsed,0) as cntUsed
FROM `tips` `t`
LEFT
JOIN (Select tip_id,count(*) as cntUsed
from tip_usage
WHERE status='Active'
group by tip_id
) tu
ON t.id = tu.tip_id
ORDER coalesce(tu.cntUsed,0)
Since you want to left-join and include the tips that have no usage, this at least sorts them all at the top with a value of zero, which is the most accurate statement of the reality of what is in the tables.
SELECT tips.*, COUNT(*) AS number
FROM tip_usage
LEFT JOIN tips ON tips.id = tip_id
WHERE STATUS = "Active"
GROUP BY tip_id
ORDER BY number DESC

MYSQL join - reference external field from nested select?

Is it allowed to reference external field from nested select?
E.g.
SELECT
FROM ext1
LEFT JOIN (SELECT * FROM int2 WHERE int2.id = ext1.some_id ) as x ON 1=1
in this case, this is referencing ext1.some_id in nested select.
I am getting errors in this case that field ext1.some_id is unknow.
Is it possible? Is there some other way?
UPDATE:
Unfortunately, I have to use nested select, since I am going to add more conditions to it, such as LIMIT 0,1
and then I need to use a second join on the same table with LIMIT 1,1 (to join another row)
The ultimate goal is to join 2 rows from the same table as if these were two tables
So I am kind of going to "spread" a few related rows into one long row.
The answer to your initial question is: No, remove your sub-query and put the condition into the ON-clause:
SELECT *
FROM ext1
LEFT JOIN int2 ON ( int2.id = ext1.some_id )
One solution could be to use variables to find the first (or second) row, but this solution would not work efficiently with indexes, so you might end up with performance problems.
SELECT ext1.some_id, int2x.order_col, int2x.something_else
FROM ext1
LEFT JOIN (SELECT `int2`.*, #i:=IF(#id=(#id:=id), #i+1, 0) As rank
FROM `int2`,
( SELECT #i:=0, #id:=-1 ) v
ORDER BY id, order_col ) AS int2x ON ( int2x.id = ext1.some_id
AND int2x.rank = 0 )
;
This assumes that you have a column that you want to order by (order_col) and Left Joins the first row per some_id.
Do you mean this?
SELECT ...
FROM ext1
LEFT JOIN int2 ON int2.id=ext1.some_id
That's what the ON clause is for:
SELECT
FROM ext1
LEFT JOIN int2 AS x ON x.id = ext1.some_id