What is the equivalent of (+)<2 in ANSI SQL? - sql

I want to turn this
TableA.ColumnA(+)<2
into ANSI SQL.
I already tried:
(TableA.ColumnA<2 OR TableA.ColumnA IS NULL)
It missed one row. Despite the fact that its ColumnA is (null).
Edit (more context):
Here is the query
SELECT * FROM a, c
WHERE a.status(+)<2
AND a.rank(+)=1
AND c.id=a.id(+)

give this a try
SELECT * FROM c LEFT JOIN a
ON c.id = a.id
AND a.status < 2
AND a.rank = 1

Related

Select records where every record in one-to-many join matches a condition

How can I write a SQL query that returns records from table A only if every associated record from table B matches a condition?
I'm working in Ruby, and I can encode this logic for a simple collection like so:
array_of_A.select { |a| a.associated_bs.all? { |b| b.matches_condition? } }
I am being generic in the construction, because I'm working on a general tool that will be used across a number of distinct situations.
What I know to be the case is that INNER JOIN is the equivalent of
array_of_A.select { |a| a.associated_bs.any? { |b| b.matches_condition? } }
I have tried both:
SELECT DISTINCT "A".* FROM "A"
INNER JOIN "B"
ON "B"."a_id" = "A"."id"
WHERE "B"."string' = 'STRING'
as well as:
SELECT DISTINCT "A".* FROM "A"
INNER JOIN "B"
ON "B"."a_id" = "A"."id"
AND "B"."string' = 'STRING'
In both cases (as I expected), it returned records from table A if any associated record from B matched the condition. I'm sure there's a relatively simple solution, but my understanding of SQL just isn't providing it to me at the moment. And all of my searching thru SO and Google has proven fruitless.
I would suggest the following:
select distinct a.*
from a inner join
(
select b.a_id
from b
group by b.a_id
having min(b.string) = max(b.string) and min(b.string) = 'string'
) c on a.id = c.a_id
Alternatively:
select distinct a.*
from a inner join b on a.id = b.a_id
where not exists (select 1 from b c where c.a_id = a.id and c.string <> 'string')
Note: In the above examples, only change the symbols a and b to the names of your tables; the other identifiers are merely aliases and should not be changed.

Transform a correlated subquery into a join

I want to express this:
SELECT
a.*
,b.timestamp_col
FROM weird_data_source a
LEFT JOIN weird_data_source b
ON a.id = b.id
AND b.timestamp_col = (
SELECT
MAX(sub.timestamp_col)
FROM weird_data_source sub
WHERE sub.id = a.id
AND sub.date_col <= a.date_col
AND sub.timestamp_col < a.timestamp_col
)
A couple notes here about the data:
date_col and timestamp_col aren't representing the same thing.
I'm not kidding... the data is really structured like this.
But the subquery is invalid. Netezza cannot handle the < operator in the correlated subquery. For the life of me I cannot figure out an alternative. How could I get around this?
My gut is telling me this could potentially be done with a join, but I haven't been able to be successful at this yet.
There's a dozen or so similar questions, but none of them seem to get at handling this type of inequality.
This should get you pretty close. You will get duplicate rows if there are two rows with the exact same timestamp_col that otherwise meet the criteria, but otherwise you should be good:
SELECT
a.id,
a.some_other_columns, -- Because we NEVER use SELECT *
b.timestamp_col
FROM
weird_data_source a
LEFT JOIN weird_data_source b ON
a.id = b.id
LEFT OUTER JOIN weird_data_source c ON
c.id = a.id AND
c.date_col <= a.date_col AND
c.timestamp_col < a.timestamp_col
LEFT OUTER JOIN weird_data_source d ON
d.id = a.id AND
d.date_col <= a.date_col AND
d.timestamp_col < a.timestamp_col AND
d.timestamp_col > c.timestamp_col
WHERE
d.id IS NULL
The query is basically looking for a matching row where no other matching row is found with a greater timestamp_col value - hence the d.id IS NULL. That column will only be NULL if no match is found.

SQL: Check for 'value' is not null returns TRUE when 'value' is null

I am using SQL Server 2008, and when I perform a left join to a table where the outer table does not have any records then I am seeing weird behavior from my where clause. If I do a check for a value from my outer table being 'not null' it sometimes returns true.
select *
from foo f left join bar b on b.id=f.id
where f.id=#id and (f.status = 1 or b.Price is not null)
When my f.status = 0 and b.Price does not exist (or, appears as null in the select) this query selects records where f.status = 0 and b.Price is null, even though (FALSE OR FALSE) should be FALSE.
If I just perform this query, it works as expected and anything without a record in 'bar' does not get selected.
select *
from foo f left join bar b on b.id=f.id
where f.id=#id and b.Price is not null
Having b.Price is not null as part of an or operation seems to be causing issue for some reason. What could be wrong with this query? I run the same query with similar data on a SQL Server 2012 machine and do not see this issue, could it be related the the version of SQL Server I am using?
These two formulations are not the same, as you have discovered.
In the first query, price can be NULL for two reasons:
There is no match from the left join.
There is a match and b.Price is null
I highly recommend the second approach, putting the condition in the on clause. However, if you do use the first one, make the comparison to a column used in the join:
where f.id = #id and (f.status = 1 or b.id is not null)
You could try an OUTER APPLY like this:
SELECT *
FROM foo f
OUTER APPLY (
SELECT *
FROM bar b
WHERE f.id = b.id
AND (
f.STATUS = 1
OR b.Price IS NOT NULL
) b
)
WHERE f.id = #id
And I also suggest using the columns instead of *, bad practice. The Outer Apply is sort of like a left join and in this case it will filter all the data from the bar table and bring you back only the data you need.
Would a CASE statement work?
IE
SELECT
(etc etc code)
CASE WHEN b.Price is not null THEN 1 ELSE 0 END AS [MyBooleanCheck]
FROM (etc etc code)

Nice way to query for cross table

I have 3 tables :
A(k1,A) B(k1,k2,B) and C(k2,C).
I want to filter all A that satisfy C.k2 condition. in this example, I must filter go through table B : filter all B that have same k1 attribute with A , and filter all C k2 attribute with B (that I have filtered before).
I have an ugly way to do this :
select * from A where k1 in (select * .....) // it looks ugly and hard to trace
I have though about using join function, but don't really know how to do this. Please tell me a best way for this query.
Thanks :)
Try this Query.
select * from A
join b on a.k1 = b.k1
join c on c.k2 = b.k2
Explanation for JOIN
It sounds pretty easy:
select * from A
join B on B.k1 = A.k1
join C on C.k2 = B.k2
If I'm reading your table structure correctly, the join logic would be like this:
SELECT *
FROM A
JOIN B
ON A.k1 = B.k1
JOIN C
ON B.k2 = C.k2
You could of course then specify in the SELECT which table you want values from, ie:
SELECT A.*,C.*
Or Limit results with WHERE ie:
WHERE C.C = 'something'
Using join to retrieve data from two or more tables. see Join Fundamentals
SELECT A.k1,B.k2
FROM A
JOIN B ON A.k1 = B.k1
JOIN C ON B.k2 = C.k2

Returning DISTINCT rows from database query

I am fairly new to SQL so apologies if there is a simple solution to this.
I have this piece of SQL that performs a join on 3 tables.
SELECT a.group_leader, b.forum_name
FROM flightuser_group a
INNER JOIN flightacl_groups c ON a.group_id = c.group_id
JOIN flightforums b ON c.forum_id = b.forum_id
WHERE a.user_id = '60'
ORDER BY a.group_leader DESC
This query returns this:
group_leader forum_name
1 tmpSQJ
0 jobby7
0 jobby5
0 tmpSQJ
I am trying to only keep the first tmpSQJ entry and remove the second but cannot determine where the DISTICT clause goes.
Many thanks in advance.
Try This:
SELECT MAX(a.group_leader), b.forum_name
FROM flightuser_group a INNER JOIN flightacl_groups c ON a.group_id = c.group_id
JOIN flightforums b ON c.forum_id = b.forum_id
WHERE a.user_id = '60'
GROUP BY b.forum_name
ORDER BY a.group_leader DESC
For MySQL, add a LIMIT 1 after the ORDER BY.
For MS SQL, add a TOP 1 after the SELECT.
These two flavors will get you only the first record in the recordset.
You could try a GROUP BY which essentially behaves the same:
SELECT a.group_leader, b.forum_name
FROM flightuser_group a
INNER JOIN flightacl_groups c ON a.group_id = c.group_id
JOIN flightforums b ON c.forum_id = b.forum_id
WHERE a.user_id = '60'
GROUP BY b.forum_name
ORDER BY a.group_leader DESC
You could also look at using "INNER JOIN" for flightforums