Subquery in Case statement using value from outer select statement - sql

I have got two tables, something like what is shown below
I need to write a select query, which when comes across a Mat_Name from category P11 or P12, and has last four characters anything other than 'B111' (for ex. AA-1234-B333), then it should give out Mat_Num of some other Mat_Name which has the same first eight characters, but has 'B111' as its last 4 characters ie. in case of AA-1234-B333 it should return Mat_Num as 123456, which is Mat_Num for AA-1234-B111, and not 114466.
I was thinking of using the below query, but then I couldn't quite figure out passing value of Mat_Name from outer select to the one inside the CASE statement (to get correct Mat_Num).
select tbl1.Mat_Name,
CASE
When (tbl2.Category IN ('P11','P12')) AND (RIGHT(tbl.Mat_Name,4) <> 'B111') THEN (Select tbl1.Mat_Num from tbl1 where ... ),
tbl2.Category
from tbl1
INNER JOIN tbl2 ON tb1.Mat_Num = tbl2.Mat_Num

Related

How to join 2 tables with multiple conditions each?

I have data split between 2 tables, and need to join the necessary data together for analysis.
One table Test 3 Output contains ID numbers, and the return value of the test. The other table Test Results contains the same IDs, along with their corresponding serial number and overall test result.
I need to combine these into a single table that just displays ID, serial number and test value.
Sorry in advance for the horrible SQL thats about to follow, I'm brand new to this.
I have 2 working queries that give me what I want, but I can't seem to join them together.
The first query:
select `ID`,`Serial Number` from `Test Results`t where (len(`Serial Number`)=16 and FailMode = '24V Supply FAIL')
This gets me the ID and serial number of all the tests that failed '24V supply'. It also filters out garbage serial numbers as the correct ones should have 16 digits.
The second query:
select `ID` from `Test 3 Output`o where o.`24V Supply (V)`<30
This gets me the ID and test results, and filters out some results that were greater than 30V. Note that '24V Supply(V) is the name of the column containing the test results.
Now when I try to join these with the ID, I get a syntax error. Here's what I tried:
select `ID`,`Serial Number`
from `Test Results`t
where (len(`Serial Number`)=16 and FailMode = '24V Supply FAIL')
left join (`Test 3 Output`o ON t.`ID` = o.`ID` where o.`24V Supply (V)`<30)
This gives the error:
Error: Syntax error (missing operator) in query expression (len(`Serial Number`)=16 and FailMode = '24V Supply FAIL') left join (`Test 3 Output`o ON t.`ID` = o.`ID` where o.`24V Supply (V)`<30)
I'm not sure what operator I'm missing but I had a feeling its related to the fact there's two where statements?
Can anyone offer some help?
Edit: I found a workaround since I can't use 2 where clauses with a join. I created 2 views with my 2 separate queries, and performed the join on those which got me what I wanted. I'd still like to hear a proper way of doing it though :)
You can join 2 subqueries like this:
SELECT q1.a, q1.b, q2.c
FROM (
(SELECT a, b FROM table1
WHERE b > 10) AS q1
LEFT JOIN
(SELECT a, c FROM table2
WHERE c > 20) AS q2
ON q1.a = q2.a
)
Doing the subqueries as separate query objects is easier to debug, but the query objects keep piling up...

MS Access SQL Query Not Exist

I have been stuck on trying to figure how to return data that is in one table but not the other. I thought an outter join would work, but it seems that Access does not allow that.
My SQL is returning results if a record exists in the MonthlyTargets_0_SPARTN_qry but if there is not record then no data is being returned. I would like to display a 0 if there are not records.
My sql is:
SELECT REF_TestCategory_tbl.CategoryID
,MonthlyTargets_0_SPARTN_qry.[Supervisor Id] AS TestOfficerID
,Count(MonthlyTargets_0_SPARTN_qry.[Sheet ID]) AS Actuals
,MonthlyTargets_0_SPARTN_qry.ComplianceMonth
FROM MonthlyTargets_0_SPARTN_qry
INNER JOIN (
REF_TestCategory_tbl INNER JOIN REF_TestCatalog_tbl ON REF_TestCategory_tbl.CategoryID = REF_TestCatalog_tbl.TestCategory
) ON MonthlyTargets_0_SPARTN_qry.[Test Number] = REF_TestCatalog_tbl.TestID
GROUP BY REF_TestCategory_tbl.CategoryID
,MonthlyTargets_0_SPARTN_qry.[Supervisor Id]
,MonthlyTargets_0_SPARTN_qry.ComplianceMonth
ORDER BY REF_TestCategory_tbl.CategoryID;
Which returns:
CategoryID TestOfficerID Actuals ComplianceMonth
1 3062 26 1/1/2020
1 3062 6 2/1/2020
2 3062 2 1/1/2020
3 3062 2 1/1/2020
3 3062 1 2/1/2020
if there are no records for feb, I need it to reurn 0 in Actuals
Thank you
If your 'ComplianceMonth' Values consistently exists regardless of your adjacent data(Meaning if the adjacent data returned for your ComplianceMonth is NULL) then you could do something like this.
SELECT REF_TestCategory_tbl.CategoryID,
MonthlyTargets_0_SPARTN_qry.[Supervisor Id] AS TestOfficerID,
coalesce(Count(MonthlyTargets_0_SPARTN_qry.[Sheet ID]),0) AS Actuals,
MonthlyTargets_0_SPARTN_qry.ComplianceMonth
FROM dbo.MonthlyTargets_0_SPARTN_qry RIGHT OUTER JOIN
dbo.REF_TestCategory_tbl RIGHT OUTER JOIN
dbo.REF_TestCatalog_tbl ON REF_TestCategory_tbl.CategoryID = REF_TestCatalog_tbl.TestCategory ON MonthlyTargets_0_SPARTN_qry.[Test Number] = REF_TestCatalog_tbl.TestID
GROUP BY REF_TestCategory_tbl.CategoryID, MonthlyTargets_0_SPARTN_qry.[Supervisor Id], MonthlyTargets_0_SPARTN_qry.ComplianceMonth
ORDER BY REF_TestCategory_tbl.CategoryID
Hope this Helps.
MS-Access DOES allow outer joins in its SQL. You can do both a LEFT JOIN or a RIGHT JOIN.
MS-Access does not include a statement for a full-outer-join. However, if you want to do a full-outer-join you can do it with a UNION ALL of a specific LEFT JOIN and a specific RIGHT JOIN. The instructions to do a full-outer-join are the following:
You do a “LEFT JOIN” (enclosed in a Select operation) between the two input record-lists. If one of the two input record-lists has one (or more) fields that for sure cannot be Null, that will be the left input record-list. The “ON” Boolean expression is the one that you want for the Full-Outer-Join.
If the left record-list has one (or more) fields that for sure cannot be Null, you skip this step. Otherwise, you do a Cross-Join between the left record-list and a record-list having only one record with one non-Null field (it can be exactly the same Select over “T_Numbers” in the example above, highlighted in green). The Cross-Join is enclosed in a Select that exposes all the fields from the Cross-Join operation, including the field “Num” from “T_Numbers” (with another field name, if you want).
You do a “RIGHT JOIN” having the same right input record-list from point 1. Its left record-list is either the Select from point 2, or the left input record-list from point 1, as corresponds (see point 2). The “ON” expression must be exactly the same as the one of the “LEFT JOIN” from point 1.
The “RIGHT JOIN” from point 3 is enclosed in a Select that exposes all the fields from the left and right input record-lists from point 1. This Select has the “WHERE” expression “IsNull(field)”, where “field” is either the “Num” field from point 2, or the field from left input record-list that for sure cannot be Null, as corresponds (see point 2).
You do a “UNION ALL” with the Select enclosing the “RIGHT JOIN” from point one and the Select enclosing the “RIGHT JOIN” from point 4.
More information at LightningGuide.net.

conditional IIF in a JOIN

I have the next data base:
Table Bill:
Table Bill_Details:
And Table Type:
I want a query to show this result:
The query as far goes like this:
SELECT
Bill.Id_Bill,
Type.Id_Type,
Type.Info,
Bill_Details.Deb,
Bill_Details.Cre,
Bill.NIT,
Bill.Date2,
Bill.Comt
FROM Type
RIGHT JOIN (Bill INNER JOIN Bill_Details
ON Bill.Id_Bill = Bill_Details.Id_Bill)
ON Type.Id_Type = Bill_Details.Id_Type
ORDER BY Bill.Id_Bill, Type.Id_Type;
With this result:
I'm not sure how to deal or how to include this:
Type.600,
Type."TOTAL",
IIF(SUM(Bill_Details.Deb) - Sum(Bill_Details.Cre) >= 0, ABS(SUM(Bill_Details.Deb) - Sum(Bill_Details.Cre)), "" ),
IIF(SUM(Bill_Details.Deb) - Sum(Bill_Details.Cre) <= 0, ABS(SUM(Bill_Details.Deb) - Sum(Bill_Details.Cre)), "" )
The previous code is the responsable of include new data in some fields, since all of the other fields will carry the same data of the upper register. I'll apreciate some sugestions to acomplish this.
Here is a revised version of the UNION which you removed from the question. The original query was a good start, but you just did not provide sufficient details about the error or problem you were experiencing. My comments were not meant to have you remove the problem query, only that you needed to provide more details about the error or problem. In the future if you have a UNION, make sure the each query of the UNION works separately. Then you could debug problems easier, one step at a time.
Problems which I corrected in the second query of the UNION:
Removed reference to table [Type] in the query, since it was not part of the FROM clause. Instead, I replaced it with a literal value.
Fixed FROM clause to join both [Bill] and [Bill_Details] tables. You had fields from both tables, so why would you not join on them just like in the first query of the UNION?
Grouped on all fields from table [Bill] referenced in the SELECT clause. You must either group on all fields, or include them in aggregate expressions like Sum() or First(), etc.
Replaced empty strings with Nulls for the False cases on Iif() statements.
SELECT
Bill.Id_Bill, Type.Id_Type, Type.Info,
Bill_Details.Deb,
Bill_Details.Cre,
Bill.NIT, Bill.Date2, Bill.Comt
FROM
Type RIGHT JOIN (Bill INNER JOIN Bill_Details
ON Bill.Id_Bill = Bill_Details.Id_Bill)
ON Type.Id_Type = Bill_Details.Id_Type;
UNION
SELECT
Bill.Id_Bill, 600 As Id_Type, "TOTAL" As Info,
IIF(SUM(Bill_Details.Deb) - Sum(Bill_Details.Cre) >= 0, ABS(SUM(Bill_Details.Deb) - Sum(Bill_Details.Cre)), Null ) As Deb,
IIF(SUM(Bill_Details.Deb) - Sum(Bill_Details.Cre) <= 0, ABS(SUM(Bill_Details.Deb) - Sum(Bill_Details.Cre)), Null ) As Cre,
Bill.NIT, Bill.Date2, Bill.Comt
FROM Bill INNER JOIN Bill_Details
ON Bill.Id_Bill = Bill_Details.Id_Bill
GROUP BY Bill.Id_Bill, Bill.NIT, Bill.Date2, Bill.Comt;

Write an additional column to query result with different values everytime

I've been searching for quite a while now and I haven't been able to find an answer for what I was looking. I have the following query:
SELECT DISTINCT o.titulo, o.fecha_estreno
FROM Obra o
WHERE (o.titulo LIKE '%Barcelona%' AND EXISTS(SELECT p.id_obra FROM Pelicula p WHERE p.id_obra = o.id_obra)) OR EXISTS(SELECT DISTINCT pa.id_obra
FROM Participa pa
WHERE pa.id_obra = o.id_obra AND EXISTS(SELECT DISTINCT l.nombre FROM Lugar l
WHERE l.nombre LIKE '%Barcelona%' AND EXISTS(SELECT DISTINCT tl.id_lugar FROM TieneLugar tl
WHERE tl.id_lugar = l.id_lugar AND tl.id_profesional = pa.id_profesional))) OR EXISTS(SELECT DISTINCT er.id_obra
FROM EstaRelacionado er
WHERE er.id_obra = o.id_obra AND EXISTS(SELECT DISTINCT k.keyword
FROM Keywords k
WHERE k.id_keyword = er.id_keyword AND k.keyword LIKE '%Barcelona%'));
What it basically does is it searches for every movie in my database which is related in some way to the city it gets. I wanted to have a third column showing for every result, with the reason the row is showing as a result (for example: TITLE CONTAINS IT, or ACTOR FROM THE MOVIE BORN THERE, etc.)
Thank you for your patience and help!
EDIT: As suggested, here are some examples of output. The column should show just the first cause related to the movie:
TITULO FECHA_ESTRENO CAUSE
---------- ---------------- ----------
Barcelona mia 1967 TITLE
https://www.postgresql.org/docs/7.4/static/functions-conditional.html
The SQL CASE expression is a generic conditional expression, similar
to if/else statements in other languages:
CASE WHEN condition THEN result
[WHEN ...]
[ELSE result]
END
CASE clauses can be used wherever an expression is valid. condition is an expression that returns a boolean result. If
the result is true then the value of the CASE expression is the result
that follows the condition. If the result is false any subsequent WHEN
clauses are searched in the same manner. If no WHEN condition is true
then the value of the case expression is the result in the ELSE
clause. If the ELSE clause is omitted and no condition matches, the
result is null.
Example for your case:
SELECT (CASE WHEN EXISTS(... l.nombre LIKE '%Barcelona%') THEN 'TITLE CONTAINS IT' WHEN <conditon for actor> THEN 'ACTOR WA BORN THERE' WHEN ... END) as reason
Here is one solution.
Create a subquery for each search condition.
include the reason in the subqueries' projections
outer join the subqueries so it doesn't matter which one hist
filter to make sure that at least one of your subqueries has a positive result
use coalesce() to get one reason.
I haven't done all your conditions, and I've probably mangled your logic but this is the general idea:
SELECT o.titulo
, o.fecha_estreno
, coalesce(t1.reason, t2.reason) as reason
FROM Obra o
left outer join ( select id_obra, 'title contains it' as reason
from Obra
where titulo LIKE '%Barcelona%' ) t1
on t1.id_obra o.id_obra
left outer join ( select distinct pa.id_obra , 'takes place there' as reason
from Participa pa
join TieneLugar tl
on tl.id_profesional = pa.id_profesional
join Lugar l
on tl.id_lugar = l.id_lugar
where l.nombre LIKE '%Barcelona%' ) t2
on t2.id_obra o.id_obra
WHERE t1.id_obra is not null
or t2.id_obra is not null
/
coalesce() just returns the first non-null value which means you won't see multiple reasons if you get more than one hit. So order the arguments to put the most powerful reasons first.
Also, you should consider consider using Oracle Text. It's the smartest way to wrangle this sort of keyword searching. Find out more.

CONCAT value in ON condition of JOIN

I have two tables.
Table: Geonames:
Country (2 character ISO code, e.g. SE)
AdminArea (Char code, e.g. 0330)
Table AdminAreas
AdminCode, (Combination of Coutry and AdminArea, e.g. "SE.0330")
So the ID of the AdminAreas that I want to join the tables on is a combination of columns on the first table. To join it I will need to join the two values from the Geonames table. Something like.
SELECT
geoname.geonameid, geoname.name, geoname.latitude, geoname.longitude,
geoname.country, geoname.admin1, admin_one.admin_id, admin_one.geoname_id
FROM geoname
INNER JOIN admin_one ON admin_one.admin_id = CONCAT(geoname.country, '.', geoname.admin1)
WHERE country='SE' LIMIT 10
Unfortunately, this is not working. It does not seem like i can CONCAT or do string_agg() on a JOIN. How do I get this JOIN working?
Your code is fine. Perhaps one issue is that you have spaces or some other character. I would recommend investigating this using:
SELECT gn.geonameid, gn.name, gn.latitude, gn.longitude,
gn.country, gn.admin1, ao.admin_id, ao.geoname_id
FROM geoname gn LEFT JOIN
admin_one ao
ON ao.admin_id = CONCAT(TRIM(gn.country), '.', TRIM(gn.admin1))
WHERE gn.country = 'SE'
LIMIT 10;
This will return even unmatched results (because of the LEFT JOIN). That might help you investigate the issue.