Create a flag in a left join - sql

I am trying to create a new column (a sort of identifier flag) for the "Null" rows resulting of my following left join :
with CTE (...) as (
... unrelated code
) select * from CTE
left join (select columnID from table1) Pu
on CTE.columnID = Pu.columnID
left join (select case when bz.column2 is null then 'null test is working' else columnID2, column2 end FROM table2) Bz
ON CTE.columnID2 = Bz.columnID2
This code is working properly when I don't try to use a 'case when'. Actually, you could very well ignore the first left join.
My purpose would be to be able test the left join result while doing it, and act depending on the result :
If the left join result give a null row : creation of a flag column for the row,
If the left join result give a normal row : the left join is done normally, and the flag column is empty (as I suspect it cant be un existent).
I'd be glad if you could give me a hand!
EDIT : tables example:
CTE
| columnID | columnID2 | InformationsCTE |
| ab | mp | randominfo1 |
| ac | ma | randominfo2 |
| ae | me | randominfo3 |
| ad | mb | randominfo4 |
table2
| columnID2 | InformationsTable2 |
| mp | randominfo5 |
| ma | randominfo6 |
| me | randominfo7 |
Result after the second left join :
new CTE
| columnID | columnID2 | InformationsCTE | InformationsTable2| FLAG |
| ab | mp | randominfo1 | randominfo5 | OK |
| ac | ma | randominfo2 | randominfo6 | OK |
| ae | me | randominfo3 | randominfo7 | OK |
| ad | mb | randominfo4 | NULL | NOK |

Just use
T-SQL:
SELECT ISNULL(Column_to_check,'flag') FROM SomeTable
PL/SQL:
SELECT NVL(Column_to_check,'flag') FROM SomeTable
Also use NVL2 as below if you want to return other value from the Column_to_check:
NVL2(Column_to_check, value_if_NOT_null, value_if_null )

Would it not be more practical to SELECT this column, use the ISNULL operator and just use a straightforward LEFT JOIN? I feel like you're over-complicating it a bit.
Something like:
with CTE (...) as (
... unrelated code
)
SELECT CTE.*, NVL(bz.InformationsTable2, 'TEST OK')
FROM CTE
LEFT JOIN table2 Bz ON CTE.columnID2 = Bz.columnID2
EDIT: Based on your example table, if you join on the ID, then use NVL on the other column, it should work for you.
Here is an example I prepared for a previous question: SQL Fiddle
Example was build in mysql, so beware syntax, but logically it works the same way

Why the joins? It seems you only want to look up data in other table, for which you'd use EXISTS or IN:
with cte (...) as (
... unrelated code
)
select
cte.*,
case when columnid in (select columnid from table1) then 'okay' else 'fail' end as test1,
case when columnid2 in (select columnid2 from table2) then 'okay' else 'fail' end as test2
from cte;

Related

Linking tables on multiple criteria

I've got myself in a bit of a mess on something I'm doing where I'm trying to get two tables linked together based on multiple bits of info.
I want to link one table to another based on the basic rules of(in this hierarchy)
where main linking is where orderid matches between the two tables
records from table 2 where valid=Y,
from those i want the valid records which has the highest seqn1 number and then from those the one that has the highest seqn2 value
table1
orderid | date | otherinfo
223344 | 22/10/2020 | okokkokokooeodijjf
table2
orderid | seqn1 | seqn2 | valid | additonaldata
223344 | 1 | 3 | y | sdfsfsf
223344 | 2 | 1 | y | sffferfr
223344 | 2 | 2 | y | sfrfrefr -- This row
223344 | 2 | 3 | n | rfrg66rr
223344 | 2 | 4 | n | adwere
223344 | 3 | 4 | n | adwere
so would want the final record to be
orderid | date | otherinfo | seqn1 | seqn2 | valid | additonaldata
223344 | 22/10/2020 | okokkokokooeodijjf | 2 | 2 | y | sfrfrefr
I started off with the code below but I'm not sure I'm doing it right and I can't seem to get it to pay attention to the valid flag when i try to add it in.
SELECT * FROM table1
left JOIN table2
ON table1.orderid = table2.orderid
AND table2.seqn1 = (SELECT MAX(table2.seqn1) FROM table2 WHERE table1.orderid = table2.orderid)
AND table2.seqn2 = (SELECT MAX(table2.seqn2) FROM table2 WHERE table1.orderid = table2.orderid
AND table2.seqn1 = (SELECT MAX(table2.seqn1) FROM table2 WHERE table1.orderid = table2.orderid))
Could someone help me amend the code please.
Use row_number analytic function with partition by orderid and order by SEQNRs in the order you need. No need for multiple subselects. To add more selections for the single row, use CASE to map your values to numbers and order by them also.
Fiddle here.
with l as (
select *,
rank() over(partition by orderid order by seqn1 desc, seqn2 desc) as rn
from line
where valid = 'y'
)
select *
from header as h
join l
on h.orderid = l.orderid
and l.rn = 1
How about something like this:
;
with cte_table2 as
(
SELECT ordered
,MAX(seqn1) as seqn1
,MAX(seqn2) as seqn2
FROM table2
where valid = 'y'
group by ordered --check if you need to add 'valid' to the group by but I don't think so.
)
SELECT
t1.*
,t3.otherinfo
--,t3.[OtherFields]
from table1 t1
inner join cte_table2 t2 on t1.orderid = t2.orderid -- first match on id
left join table2 t3 on t3.orderid = t2.orderid and t3.seqn1 = t2.seqn1 and t3.seqn2 = t2.seqn2

Retrieve data from the same table if subId is an id of other item

I have a table that contains some records, and I would like to get only these records that have subID to a record with the id of the subID value. If there is no row with the id then do not take this row to the table. Also do not duplicate values if already in the table and do not look at rows that have subId 0 because they are as parents we can say so they do not have childs
----------------------------
ID | SUBID | NAME | ENABLED |
30 | 0 | EXP1 | TRUE |
55 | 30 | EXP2 | TRUE |
70 | 30 | EXP3 | FALSE |
99 | 42 | EXP4 | FALSE |
232| 0 | EXP5 | TRUE |
65 | 232 | EXP6 | TRUE |
-----------------------------
Expected result:
----------------------------
ID | SUBID | NAME | ENABLED |
30 | 0 | EXP1 | TRUE |
55 | 30 | EXP2 | TRUE |
70 | 30 | EXP3 | FALSE |
232| 0 | EXP5 | TRUE |
65 | 232 | EXP6 | TRUE |
-----------------------------
If someone could help me how to write this SQL statement in a good way I will be grateful.
You can use 'Exists':
SELECT T1.* FROM TEST T1
WHERE EXISTS (SELECT T2.ID FROM TEST T2 WHERE T2.ID = T1.SUBID)
OR EXISTS (SELECT T3.SUBID FROM TEST T3 WHERE T3.SUBID = T1.ID)
Test Result:
DB<>Fiddle
How about a union
select a.*
from have a
inner join have b
on a.subid=b.id
union
select b.*
from have a
inner join have b
on a.subid=b.id;
This can be actually pretty complicated, as you've evidently found out. I'd suggest a CTE and a UNION with your JOINs and aliases. It also looks like it'll need all that in a subquery to do a DISTINCT, too.
Without testing this, I'd image it looks something like this:
WITH MAIN AS (
SELECT ID, SUBID, NAME
FROM TABLE t
WHERE ENABLED = TRUE
)
SELECT DISTINCT ID, NAME
FROM (
SELECT ID, NAME
FROM MAIN
UNION
SELECT t.ID, t.NAME
FROM MAIN
LEFT JOIN TABLE t on MAIN.SUBID = t.ID
WHERE MAIN.SUBID <> 0
)
The outer select might not be needed if you do a distinct on each of the inner queries, but without testing it, I can't say for sure. I'd guess it would only DISTINCT the two lists separately, which isn't your intended result.
I'm kind of hoping someone else can come up with a less complicated version. I'd also suggest you do some more research on CTEs, UNIONs, aliases, and see if you can make this simpler on your own. But this should get you in the right direction.
BTW, I used a CTE (WITH MAIN AS) so that the query wouldn't be duplicated.
Try this script-
SELECT YT.*
FROM your_table YT
INNER JOIN (
SELECT DISTINCT(B.ID)
FROM your_table A
LEFT JOIN your_table B
ON A.SUBID = B.ID
WHERE B.ID IS NOT NULL
) C ON YT.ID = C.ID OR YT.SUBID = C.ID
By my understanding of what you are trying to do, you simply want:
SELECT * FROM myTable t1
WHERE SubID = 0
OR EXISTS (SELECT NULL FROM myTable t2 WHERE t2.id = t1.SubID)

MySQL - Join tables, retrieve only Max ID

I've seen solutions for something similar on other posts, but I've been having an issue applying it to my specific problem.
Here is my initial join:
SELECT service_note_task, comment_id, comment FROM service_note_task LEFT JOIN service_note_task_comments ON service_note_task.service_note_task_id = service_note_task_comments.service_note_task_id;
Which results in:
+-----------------------------+------------+--------------+
| service_note_task | comment_id | comment |
+-----------------------------+------------+--------------+
| This is service note task 3 | 25 | Comment |
| This is service note task 3 | 26 | Comment Blah |
| This is service note task 3 | 36 | aaa |
| This is service note task 2 | 13 | Awesome comm |
| This is service note task 1 | 12 | Cool Comm |
+-----------------------------+------------+--------------+
But for each service_note_task, I really only need one row representing the comment with the highest comment_id, like this:
+-----------------------------+------------+--------------+
| service_note_task | comment_id | comment |
+-----------------------------+------------+--------------+
| This is service note task 3 | 36 | aaa |
| This is service note task 2 | 13 | Awesome comm |
| This is service note task 1 | 12 | Cool Comm |
+-----------------------------+------------+--------------+
I figure I could use MAX in a sub-select statement to narrow down the results as I want them. How can I incorporate that into my statement to get these results?
For reference, this is known as "groupwise-maximum"
http://dev.mysql.com/doc/refman/5.0/en/example-maximum-column-group-row.html
since you haven't mention the RDBMS you are using, this query below mostly works on many RDBMS (not all)
SELECT a.*, b.* -- select only the columns you want.
FROM service_note_task a
INNER JOIN service_note_task_comments b
ON a.service_note_task_id = b.service_note_task_id
INNER JOIN
(
SELECT service_note_task_id, MAX(commentID) max_ID
FROM service_note_task_comments
GROUP BY service_note_task_id
) c ON b.service_note_task_id = c.service_note_task_id AND
b.commentID = c.max_ID
if your RDBMS supports Analytical Functions, you can use this below,
SELECT a.service_note_task, b.comment_id, b.comment
FROM service_note_task a
INNER JOIN
(
SELECT service_note_task_id, comment_id, comment,
ROW_NUMBER() OVER (PARTITION BY service_note_task_id
ORDER BY comment_id DESC) rn
FROM service_note_task_comments
GROUP BY
) c ON a.service_note_task_id = b.service_note_task_id AND
b.rn = 1
try:
SELECT service_note_task, comment_id, comment
FROM service_note_task SNT1
LEFT JOIN service_note_task_comments ON service_note_task.service_note_task_id = service_note_task_comments.service_note_task_id
WHERE comment_id = (SELECT MAX(comment_id) FROM service_note_task SNT2 WHERE SNT1.service_note_task = SNT2.service_note_task);
SELECT service_note_task, comment_id, comment
FROM service_note_task s LEFT JOIN service_note_task_comments sc
ON s.service_note_task_id = sc.service_note_task_id;
WHERE EXISTS (
SELECT 1
FROM service_note_task_comments s2
WHERE s.service_note_task_id = s2.service_note_task_id
HAVING MAX(s2.comment_id) = sc.comment_id
)

Remove NULL values from multiple LEFT JOIN in sql server

I have the following tables
ITEM1
ID | NAME | GEARS | ITEM2_ID |
-------------------------------
1 | Test | 56 | 4 |
2 | Test2| 12 | 2 |
ITEM3
ID | NAME | DATA | ITEM2_ID |
-------------------------------
1 | Test | 1 | 1 |
2 | Test7| 22 | 3 |
ITEM2
ID | VALUE |
--------------------
1 | is simple |
2 | is hard |
3 | is different|
4 | is good |
5 | very good |
And my query
SELECT TOP(3) * FROM (
SELECT ID,
rankTable.RANK as RANK_,
TOTALROWS = COUNT(*) OVER()
FROM ITEM2
INNER JOIN
CONTAINSTABLE(ITEM2, [VALUE], 'ISABOUT("good")') as rankTable
ON ITEM2.ID = rankTable.[KEY]
) as ITEM2table
LEFT JOIN (
SELECT ID,
NAME,
GEARS,
ITEM2_ID
FROM ITEM1
) as ITEM1table
ON ITEM1table.ITEM2_ID = ITEM2table.ID
LEFT JOIN (
SELECT ID,
NAME,
DATA,
ITEM2_ID
FROM ITEM3
) as ITEM3table
ON ITEM3table.ITEM2_ID = ITEM2table.ID
and the results
How to remove (if is possible) the first row (ID = 5) using the above SQL query ? Also I want to show TOTALROWS = 1 because other row contains NULL's except first 3 columns.
Thank you.
If I understand correctly, you want to keep only the rows where either the first or the second (or both) outer join succeeds:
WHERE ITEM1table.ITEM2_ID IS NOT NULL
OR ITEM3table.ITEM2_ID IS NOT NULL
Some simplifications can be done on the query. No need for the nested subqueries:
SELECT TOP(3)
ITEM2table.ID,
rankTable.RANK as RANK_,
TOTALROWS = COUNT(*) OVER(),
ITEM1table.*,
ITEM3table.*
FROM
ITEM2
INNER JOIN
CONTAINSTABLE(ITEM2, [VALUE], 'ISABOUT("good")') as rankTable
ON ITEM2.ID = rankTable.[KEY]
LEFT JOIN
ITEM1 as ITEM1table
ON ITEM1table.ITEM2_ID = ITEM2.ID
LEFT JOIN
ITEM3 as ITEM3table
ON ITEM3table.ITEM2_ID = ITEM2.ID
WHERE ITEM1table.ITEM2_ID IS NOT NULL
OR ITEM3table.ITEM2_ID IS NOT NULL
ORDER BY something --- you need to order by something
--- if you use TOP. Unless you want
--- 3 (random) rows.
Maybe there's an obvious reason, but if you want to eliminate rows where the second table doesn't have a match, why are you using a left join? It seems like your first join should be an inner join and your second should be left - that would give you the results you want in this case.
You can either use INNER JOIN instead of LEFT JOIN, or put
WHERE ITEM1table.ID IS NOT NULL AND ITEM3table.ID IS NOT NULL
at the end of your query

MySQL query help (involving joins?)

Although I've figured out several queries that almost do this, I can't quite get it perfectly and I'm getting frustrated. Here is the setup:
Table: Issue
| id | name | value |
+-------------------+
| 1 | a | 10 |
| 2 | b | 3 |
| 3 | c | 4 |
| 4 | d | 9 |
Table: Link
| source | dest |
+---------------+
| 1 | 2 |
| 1 | 3 |
The link table sets up a source/dest relationship between rows in the issue table. Yes, I know this is normalized terribly, but I did not create this schema even though I now have to write queries against it :(.
What I want is results that look like this:
| name | value |
+--------------+
| a | 17 |
| d | 9 |
The values in the results should be the sum of the values in the issue table when you aggregate together a source with all its dests along with the name of the source.
Some notes
(1) A source->dest is a one->many relationship.
(2) The best answer will not have any hardcoded id's or names in the query (meaning, it will be generalized for all setups like this).
(3) This is in MySQL
Thank you and let me know if I should include any more information
Its fairly simple, but the stickler is the fact that A is not a destination of A yet it is included in the table. The robust solution would involve modifying the data to add
Table: Link
| source | dest |
+---------------+
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
Then a simple
SELECT a.name, SUM(d.value) FROM
Issues as a
JOIN Link as b on a.id=b.source
JOIN Issues AS d on b.dest=d.id;
GROUP BY a.name;
If you can't modify the data.
SELECT a.name, SUM(d.value)+a.value FROM
Issues as a
JOIN Link as b on a.id=b.source
JOIN Issues AS d on b.dest=d.id;
GROUP BY a.name,a.value;
MAY work.
SELECT S.name, S.value + SUM(D.value) as value
FROM Link AS L
LEFT JOIN Issue AS S ON L.source = S.id
LEFT JOIN Issue AS D ON L.dest = D.id
GROUP BY S.name
You could use a double join to find all linked rows, and add the sum to the value of the source row itself:
select src.name, src.value + sum(dest.value)
from Issue src
left join Link l
on l.source = src.id
left join Link dest
on dest.id = l.dest
group by src.name, src.value
This one should return the SUM of both source and dests, and only return items which are source.
SELECT s.name, COALESCE( SUM(d.value), 0 ) + s.value value
FROM Issue s
LEFT JOIN Link l ON ( l.source = s.id )
LEFT JOIN Issue d ON ( d.id = l.dest )
WHERE s.id NOT IN ( SELECT dest FROM Link )
GROUP BY s.name, s.value
ORDER BY s.name;