SQL - displaying zero results - sql

I am looking to display a 'O' or 'No results found' when no records are found based off my query but can't get it right. I think the problem comes from that I am trying to display information about the records I am counting along with how many there are that fit the criteria. Perhaps its not the count that I need to change with ISNULL but something to modify the result if no rows are outputted.
Thanks for any help in advance.
select t3.countyname, t2.titleunitname, t1.transdesc, count (isnull(t3.orderkey,0)) as c1
from tblorder as t3
left join qryOrderRecord1 as t1 on t3.OrderKey = t1.OrderKey
left join qrytitleunits as t2 on t3.titleunitnum = t2.titleunitnum
WHERE t1.transdesc = 'LETTERS'
AND (t3.OrderDateTime between '7/7/2010' AND '7/7/2010')
AND t3.countyname = 'Frankfurt'
AND t2.titleunitname = 'Maxwell'
group by t3.countyname,t1.transdesc, t2.titleunitname

That count(isnull(column, 0)) is the same as count(1) or count(*). See this question for the why...
Count(*) vs Count(1)
Maybe you want count(t1.orderkey)? This would not count any null t1.orderkey's.

I think you need to count t1.orderkey t3 is on the left side of the outer join so it is the t1.orderkey values that will be NULL.
Also I moved the Filters on t1 and t2 into the JOIN conditions not the WHERE clause
If that doesn't do the trick providing some sample data in your question will help.
SELECT t3.countyname,
t2.titleunitname,
t1.transdesc ,
COUNT(t1.orderkey) AS c1 /*As dotjoe's answer suggests*/
FROM tblorder AS t3
LEFT JOIN qryOrderRecord1 AS t1
ON t3.OrderKey = t1.OrderKey
AND t1.transdesc = 'LETTERS'
LEFT JOIN qrytitleunits AS t2
ON t3.titleunitnum = t2.titleunitnum
AND t2.titleunitname = 'Maxwell'
WHERE t3.OrderDateTime BETWEEN '7/7/2010' AND '7/7/2010' /*Seems odd! Should these be
different dates?*/
AND t3.countyname = 'Frankfurt'
GROUP BY t3.countyname,
t1.transdesc ,
t2.titleunitname

Give this a shot.
select t3.countyname, t2.titleunitname, t1.transdesc, count (isnull(t3.orderkey,0)) as c1
into #Tmp
from tblorder as t3
left join qryOrderRecord1 as t1 on t3.OrderKey = t1.OrderKey
left join qrytitleunits as t2 on t3.titleunitnum = t2.titleunitnum
where t1.transdesc = 'LETTERS'
AND (t3.OrderDateTime between '7/7/2010' AND '7/7/2010')
AND t3.countyname = 'Frankfurt'
AND t2.titleunitname = 'Maxwell'
group by t3.countyname,t1.transdesc, t2.titleunitname
if ##ROWCOUNT > 0
select *
from #Tmp
else
select 'No Results Found.'

Related

Return only duplicate values in Access SQL

I am having trouble trying to return only duplicate values in a query on Microsoft Access.
Here is my initial query:
SELECT tbl_dbextract_GP.GPNo,
[tbl_dbextract_GP].[GPFirst]+" "+[tbl_dbextract_GP].[GPLast] AS GPName,
Sum(tbl_main_ListLog.count_ToImport) AS ImportCount,
tbl_main_ListLog.SearchSequence
FROM ((tbl_main_ListLog
INNER JOIN tbl_dbextract_GPPractice
ON tbl_main_ListLog.GPPracticeID = tbl_dbextract_GPPractice.Id)
INNER JOIN tbl_dbextract_GP
ON tbl_dbextract_GPPractice.GPId = tbl_dbextract_GP.Id)
WHERE tbl_dbextract_GP.GPNo <> 'G0000'
GROUP BY tbl_dbextract_GP.GPNo,
[tbl_dbextract_GP].[GPFirst]+" "+[tbl_dbextract_GP].[GPLast],
tbl_main_ListLog.SearchSequence;
I only want to return results where there is at least 2 entries per GPNo.
The search sequence is incremental, so basically I am trying to find entries where there has been at least 2 searches.
This was my attempt:
SELECT
tbl_dbextract_GP.GPNo,
[tbl_dbextract_GP].[GPFirst] + " " + [tbl_dbextract_GP].[GPLast] AS GPName,
Sum(tbl_main_ListLog.count_ToImport) AS ImportCount,
tbl_main_ListLog.SearchSequence
FROM (((tbl_main_ListLog
INNER JOIN tbl_dbextract_GPPractice
ON tbl_main_ListLog.GPPracticeID = tbl_dbextract_GPPractice.Id)
INNER JOIN tbl_dbextract_GP
ON tbl_dbextract_GPPractice.GPId = tbl_dbextract_GP.Id)
LEFT JOIN (
SELECT tbl_main_ListLog.GPPracticeID as GPPID,
SUM(tbl_main_ListLog.SearchSequence) as SumSS
FROM tbl_main_ListLog
WHERE SumSS > 1
GROUP BY tbl_main_ListLog.GPPracticeID) SubQ
ON tbl_main_ListLog.GPPracticeID = SubQ.GPPID)
WHERE tbl_dbextract_GP.GPNo <> 'G0000'
GROUP BY tbl_dbextract_GP.GPNo,
[tbl_dbextract_GP].[GPFirst]+" "+[tbl_dbextract_GP].[GPLast],
tbl_main_ListLog.SearchSequence;
This didn't work, as it prompted me to manually enter the value for SumSS on execute, and that didn't return the right results anyway.
This is a sample output (Names redacted) - I want to return results where there is at least 2 entries per GPNo
EDIT: Modified attempt using Tim's solution - returning "Syntax error in JOIN operation":
SELECT
t3.GPNo,
t3.GPFirst + " " + t3.GPLast AS GPName,
SUM(t1.count_ToImport) AS ImportCount,
t1.SearchSequence
FROM (((tbl_main_ListLog t1
INNER JOIN tbl_dbextract_GPPractice t2
ON t1.GPPracticeID = t2.Id)
INNER JOIN tbl_dbextract_GP t3
ON t2.GPId = t3.Id)
INNER JOIN
(
SELECT t3.GPNo
FROM ((tbl_main_ListLog t1
INNER JOIN tbl_dbextract_GPPractice t2
ON t1.GPPracticeID = t2.Id)
INNER JOIN tbl_dbextract_GP t3
ON t2.GPId = g3.Id)
GROUP BY t3.GPNo
HAVING COUNT(*) > 1
) t4
ON t3.GPNo = t4.GPNo)
WHERE
t3.GPNo <> 'G0000'
GROUP BY
t3.GPNo,
t3.GPFirst + " " + t3.GPLast,
t1.SearchSequence;
Given that this is MS Access, which does not support analytic functions, doing a join to another subquery to impose the restriction on the number of searches seems like a reasonable approach, and your second attempt does not look far off. But you should be using the HAVING operator instead of WHERE, with a few other changes:
SELECT
t3.GPNo,
t3.GPFirst + " " + t3.GPLast AS GPName,
SUM(t1.count_ToImport) AS ImportCount,
t1.SearchSequence
FROM tbl_main_ListLog t1
INNER JOIN tbl_dbextract_GPPractice t2
ON t1.GPPracticeID = t2.Id
INNER JOIN tbl_dbextract_GP t3
ON t2.GPId = t3.Id
INNER JOIN
(
SELECT t3.GPNo
FROM tbl_main_ListLog t1
INNER JOIN tbl_dbextract_GPPractice t2
ON t1.GPPracticeID = t2.Id
INNER JOIN tbl_dbextract_GP t3
ON t2.GPId = t3.Id
GROUP BY t3.GPNo
HAVING COUNT(*) > 1
) t4
ON t3.GPNo = t4.GPNo
WHERE
t3.GPNo <> 'G0000'
GROUP BY
t3.GPNo,
t3.GPFirst + " " + t3.GPLast,
t1.SearchSequence;
In the subquery which I have aliased as t4, I do the same set of joins as your original query, but I then aggregate only by the GPNo. Also, I added a HAVING clause requiring that a GPNo appear two or more times in order to be retained in the result set. This subquery then filters off non matching GPNo records in your original query.
Add the Count(1) As Countr to your query and apply condition Countr>1 (apply HAVING clause as explained above).
Alternatively, in order to simplify your task, you can save the original working query as the intermediate one, e.g. Sub_With_Duplicates (Access has that feature), and then build another one (eg. Qry_No_Duplicates on the top of the intermediate and simply apply WHERE Countr>1 clause to that field. This should work.

ORACLE SQL - Get limited rows with maximum values in a column with rownum but without a subquery

I have a table in Oracle DB which consists of products and stock. I want to get a limited number of products in output (say 10 products) with maximum stock. There are also other conditions that I would check which involves inner join with multiple tables.
This query randomly selects 10 products from the table then sorts it, so its not helpful:
Select prod_code, stock from producttable where rownum < 10
--and lots of other conditions
order by stock desc
I searched and found this below method. But this runs forever because the inner query is a full table output:
Select * from (Select prod_code, stock from producttable where
-- lots of other conditions
order by stock desc) where rownum < 10
Can someone please help me find a way to do this accurately and efficiently ?
Following is the query used -
SELECT * from (SELECT
wbob.p1
FROM t1 wbob
Inner join t2 wboc on wboc.p2 = wbob.p2
Inner join t3 wboa on wboa.p2 = wbob.p2
Inner join t4 mfa on mfa.p3 = wbob.p4
Left outer join t5 mfb on mfb.p3 = wbob.p4
Inner join t6 mfc on mfc.p3 = wbob.p4
Inner join t7 mfd on mfd.p3 = wbob.p4
Inner join t8 mfg on mfg.p3 = wbob.p4
Inner join t9 sta on sta.p5 = wbob.p4
Inner join t10 stb on stb.p6 = sta.p6
Inner join t11 stc on stc.p7 = stb.p7
WHERE
wboa.stock > '0'
and wboa.p8 in ('14','198')
and wboc.p9 = '187'
and mfd.p10 > 0
and stb.p11 > 0
and trim(mfa.p12) = 'ACT'
and mfa.p13 = 'N'
and trim(stc.p7) = '3333'
and mfc.p14 = 11
and mfc.p15 = 3333
and mfg.p16 = 1
and mfc.p17 = 'Y'
and mfd.p18 = 'N'
and mfa.p19 = 'W'
and wbob.p1 NOT IN (Select wbob1.p1
from t1 wbob1
inner join t3 wboa1 ON wboa1.p2 = wbob1.p2
where wboa1.stock > '0'
and wboa1.p8 NOT IN ('14','198'))
and (wbob.p4 NOT IN (Select mfb7.p3 from t5 mfb7) OR wbob.p4 IN (Select mfb8.p3 from t5 mfb8
where mfb8.p20 = 0))
ORDER BY stb.p11 DESC) where rownum < 10
Explain plan
You can do this in 12c without a subquery using fetch first n rows only
Select prod_code, stock
from producttable
where ...
--and lots of other conditions
order by stock desc
fetch first 10 rows only
However, this will not solve the problem you are complaining about which is the performance of the query and how the inner query is a full table output. This is actually necessary and this solution will do the same. In order to sort stock to get the top stock items, the db will have to look at and sort all the possible rows. How else can you get the top items without looking at all of them? There might be ways to improve this, like an index on the stock value, but I wouldn't recommend that without knowing detail your data model.
You need a subquery:
select p.*
from (Select prod_code, stock
from producttable
order by stock desc
) p
where rownum < 10;
For performance, you want an index on producttable(stock, prod_code). The subquery isn't causing the performance issue; the lack of index is.

Query using CASE, WHEN, THEN in postgresql not working

I have three tables, tbl_doctors_details, tbl_time and tbl_token.
I have to select the details from tbl_time or tbl_token. That is if for particular doctor in hospital have t_type is time then select from tbl_time else select from tbl_token.
For example, for doctor_id is 100 and hospital_id 1, the t_type is time. So for this user, details should select from tbl_time table.
Below is the sample structure and data of three tables:
How can I use CASE condition in query here?
I tried:
SELECT *
FROM
( SELECT * CASE WHEN t_type= 'time' FROM dbname.tbl_doctors_details t1 THEN
dbname.tbl_time t2
ELSE
dbname.tbl_token t2
WHERE t1.hospital_id=t2.hospital_id AND t1.doctor_id=t2.doctor_id);
I know the query is not working, but how can I make the query working?
You need to join both tables using two LEFT JOINs and then COALESCE to find the matching data:
SELECT t1.*,
colaesce(t_time.hospid, t_token.hospid),
colaesce(t_time.start, t_token.start)
FROM dbname.tbl_doctors_details t1
LEFT JOIN dbname.tbl_time t_time
ON t1.doctor_id=t_time.doctor_id
AND t1.t_type= 'time'
LEFT JOIN dbname.tbl_token t_token
ON t1.doctor_id=t_token.doctor_id
AND t1.t_type= 'token';
Use CASE WHEN in your SELECT and LEFT JOIN both tables. In both LEFT JOIN use the condition of t_type to join on the tables. In your SELECT you can now check whether table t1 or t2 was joined and select the preferred column. Here's a sample (the selected column is just a sample to illustrate):
SELECT CASE WHEN t1.id IS NULL THEN t2.id ELSE t1.id END AS joined_id
FROM tbl_doctors_details as tbl_base
LEFT JOIN tbl_time t1 ON t1.t_type = 'time' AND t1.doc_id = tbl_base.doc_id
LEFT JOIN tbl_token t2 ON t2.t_type = 'token' AND t2.doc_id = tbl_base.doc_id
Serious smell of doing homework for someone here.. Meh
SELECT
doctors.*
, CASE
WHEN doctors.type = 'time' THEN time.start
ELSE token.start
END AS start
FROM
doctors
LEFT OUTER JOIN time
ON time.doc_id = doctors.doc_id
AND time.hospital_id = doctors.hospital_id
AND doctors.t_type = 'time'
LEFT OUTER JOIN token
ON token.doc_id = doctors.doc_id
AND token.hospital_id = doctors.hospital_id
AND doctors.t_type = 'token'

Using left join and inner join in the same query

Below is my query using a left join that works as expected. What I want to do is add another table filter this query ever further but having trouble doing so. I will call this new table table_3 and want to add where table_3.rwykey = runways_updatable.rwykey. Any help would be very much appreciated.
SELECT *
FROM RUNWAYS_UPDATABLE
LEFT JOIN TURN_UPDATABLE
ON RUNWAYS_UPDATABLE.RWYKEY = TURN_UPDATABLE.RWYKEY
WHERE RUNWAYS_UPDATABLE.ICAO = 'ICAO'
AND (RUNWAYS_UPDATABLE.TORA > 4000 OR LDA > 0)
AND (TURN_UPDATABLE.AIRLINE_CODE IS NULL OR TURN_UPDATABLE.AIRLINE_CODE = ''
OR TURN_UPDATABLE.AIRLINE_CODE = '')
'*************EDIT To CLARIFY *****************
Here is the other statement that inner join i would like to use and I would like to combine these 2 statements.
SELECT *
FROM RUNWAYS_UPDATABLE A, RUNWAYS_TABLE B
WHERE A.RWYKEY = B.RWYKEY
'***What I have so far as advice taken below, but getting syntax error
SELECT RUNWAYS_UPDATABLE.*, TURN_UPDATABLE.*, AIRPORT_RUNWAYS_SELECTED.*
FROM RUNWAYS_UPDATABLE
INNER JOIN AIRPORT_RUNWAYS_SELECTED
ON RUNWAYS_UPDATABLE.RWYKEY = AIRPORT_RUNWAYS_SELECTED.RWYKEY
LEFT JOIN TURN_UPDATABLE
ON RUNWAYS_UPDATABLE.RWYKEY = TURN_UPDATABLE.RWYKEY
NOTE: If i comment out the inner join and leave the left join or vice versa, it works but when I have both of joins in the query, thats when im getting the syntax error.
I always come across this question when searching for how to make LEFT JOIN depend on a further INNER JOIN. Here is an example for what I am searching when I am searching for "using LEFT JOIN and INNER JOIN in the same query":
SELECT *
FROM foo f1
LEFT JOIN (bar b1
INNER JOIN baz b2 ON b2.id = b1.baz_id
) ON
b1.id = f1.bar_id
In this example, b1 will only be included if b2 is also found.
Remember that filtering a right-side table in left join should be done in join itself.
select *
from table1
left join table2
on table1.FK_table2 = table2.id
and table2.class = 'HIGH'
I finally figured it out. Thanks for all your help!!!
SELECT * FROM
(AIRPORT_RUNWAYS_SELECTED
INNER JOIN RUNWAYS_UPDATABLE
ON AIRPORT_RUNWAYS_SELECTED.RWYKEY = RUNWAYS_UPDATABLE.RWYKEY)
LEFT JOIN TURN_UPDATABLE ON RUNWAYS_UPDATABLE.RWYKEY = TURN_UPDATABLE.RWYKEY
Add your INNER_JOIN before your LEFT JOIN:
SELECT *
FROM runways_updatable ru
INNER JOIN table_3 t3 ON ru.rwykey = t3.rwykey
LEFT JOIN turn_updatable tu
ON ru.rwykey = tu.rwykey
AND (tu.airline_code IS NULL OR tu.airline_code = '' OR tu.airline_code = '')
WHERE ru.icao = 'ICAO'
AND (ru.tora > 4000 OR ru.lda > 0)
If you LEFT JOIN before your INNER JOIN, then you will not get results from table_3 if there is no matching row in turn_updatable. It's possible this is what you want, but since your join condition for table_3 only references runways_updatable, I would assume that you want a result from table_3, even if there isn't a matching row in turn_updatable.
EDIT:
As #NikolaMarkovinović pointed out, you should filter your LEFT JOIN in the join condition itself, as you see above. Otherwise, you will not get results from the left-side table (runways_updatable) if that condition isn't met in the right-side table (turn_updatable).
EDIT 2: OP mentioned this is actually Access, and not MySQL
In Access, perhaps it's a difference in the table aliases. Try this instead:
SELECT [ru].*, [tu].*, [ars].*
FROM [runways_updatable] AS [ru]
INNER JOIN [airport_runways_selected] AS [ars] ON [ru].rwykey = [ars].rwykey
LEFT JOIN [turn_updatable] AS [tu]
ON [ru].rwykey = [tu].rwykey
AND ([tu].airline_code IS NULL OR [tu].airline_code = '' OR [tu].airline_code = '')
WHERE [ru].icao = 'ICAO'
AND ([ru].tora > 4000 OR [ru].lda > 0)
If it is just an inner join that you want to add, then do this. You can add as many joins as you want in the same query. Please update your answer if this is not what you want, though
SELECT *
FROM RUNWAYS_UPDATABLE
LEFT JOIN TURN_UPDATABLE
ON RUNWAYS_UPDATABLE.RWYKEY = TURN_UPDATABLE.RWYKEY
INNER JOIN table_3
ON table_3.rwykey = runways_updatable.rwykey
WHERE RUNWAYS_UPDATABLE.ICAO = 'ICAO'
AND (RUNWAYS_UPDATABLE.TORA > 4000 OR LDA > 0)
AND (TURN_UPDATABLE.AIRLINE_CODE IS NULL OR TURN_UPDATABLE.AIRLINE_CODE = ''
OR TURN_UPDATABLE.AIRLINE_CODE = '')
I am not really sure what you want. But maybe something like this:
SELECT RUNWAYS_UPDATABLE.*, TURN_UPDATABLE.*
FROM RUNWAYS_UPDATABLE
JOIN table_3
ON table_3.rwykey = runways_updatable.rwykey
LEFT JOIN TURN_UPDATABLE
ON RUNWAYS_UPDATABLE.RWYKEY = TURN_UPDATABLE.RWYKEY
WHERE RUNWAYS_UPDATABLE.ICAO = 'ICAO'
AND (RUNWAYS_UPDATABLE.TORA > 4000 OR LDA > 0)
AND (TURN_UPDATABLE.AIRLINE_CODE IS NULL OR TURN_UPDATABLE.AIRLINE_CODE = ''
OR TURN_UPDATABLE.AIRLINE_CODE = '')
For Postgres, query planner does not guarantee order of execution of join. To Guarantee one can use #Gajus solution but the problem arises if there are Where condition for inner join table's column(s). Either one would to require to carefully add the where clauses in the respective Join condition or otherwise it is better to use subquery the inner join part, and left join the output.

Problems with joins and a query

Edit: Deleted my old message cause it was confusing. And i can't answer my own question for now.
Found, problem come from GROUP BY.
After some researches, i found that we can't use GROUP BY for group a column inside grouped rows.
So this work as expected :
SELECT candidats.*
, AVG(test_results.rate_good_answer) AS toto
FROM "candidats"
LEFT JOIN "sessiontests"
ON "sessiontests"."candidat_id" = "candidats"."id"
LEFT JOIN "test_results"
ON "test_results"."sessiontest_id" = "sessiontests"."id"
LEFT JOIN "questionnaires"
ON "questionnaires"."id" = "test_results"."questionnaire_id"
WHERE (sessiontests.terminate = 't' )
AND ("questionnaires"."category" LIKE '%java%' )
GROUP BY candidats.id
ORDER BY toto
But this will grouped only my column in test_results :
SELECT candidats.*,
FROM "candidats"
LEFT JOIN "sessiontests"
ON "sessiontests"."candidat_id" = "candidats"."id"
LEFT JOIN "test_results"
ON "test_results"."sessiontest_id" = "sessiontests"."id"
LEFT JOIN "questionnaires"
ON "questionnaires"."id" = "test_results"."questionnaire_id"
WHERE (sessiontests.terminate = 't' )
AND ("questionnaires"."category" LIKE '%java%' )
GROUP BY candidats.id, test_results.rate_good_answer
ORDER BY AVG(test_results.rate_good_answer)
Edit 3 :
My problem was the second query was returning each different test_results row for my candidats, whereas i expected it to return me one line per candidat.
First query, is the answer, and it works nice.
SELECT candidats.*
, AVG(test_results.rate_good_answer) AS toto
FROM "candidats"
LEFT JOIN "sessiontests"
ON "sessiontests"."candidat_id" = "candidats"."id"
LEFT JOIN "test_results"
ON "test_results"."sessiontest_id" = "sessiontests"."id"
LEFT JOIN "questionnaires"
ON "questionnaires"."id" = "test_results"."questionnaire_id"
WHERE (sessiontests.terminate = 't' )
AND ("questionnaires"."category" LIKE '%java%' )
GROUP BY candidats.id
ORDER BY toto