Subquery without temporary tables - sql

I'm having a problem which I wanted to solve with temporary tables, however, I discovered this is not really a good approach in Oracle.
I issue a select command from multiple tables and get Result1. I then want to combine columns 2,3 into 1 column which contains the unique values from columns 2,3 e.g.
Select distinct(col2) from Result1
UNION
Select distinct(col3) from Result1
As Result2
I then want to use the values from Result2 in a subquery. An easy way to do this last part to make the query above part of my Where clause as a subquery, but again I don't know how to reference Result1. So what I want is:
Select * from xyz where col in
(Select distinct(col2) from Result1
UNION
Select distinct(col3) from Result1)
What is the best way to combine the results of these queries in Oracle without a temp table?

First, your query can be written as:
Select *
from xyz
where col in (Select col2 from Result1 union all
Select col3 from Result1
)
The in automatically handles duplicates in this case. This is better written as the following, using with for result1:
with result1 as (
<your query here>
)
select *
from xyz
where exists (select 1 from result1 r where xyz.col1 = r.col2 or xyz.col1 = r.col3);
Does that solve your problem?

Related

Find ID's not match a list

I cannot say I am so experienced with SQL. Here is my question.
A table TripEvent have millions of rows. It contains a column Bold_ID that is indexed and unique.
So I can have this query
select bold_id from TripEvent where bold_id in (354469477, 354469536, 354469500, 987359)
Result is
354469477
354469536
354469500
as those exists. But I want to reverse it. How can I get a list if id's that don't exists ?
In this case it should return one row
987359
I cannot use NOT in query as that would return all rows in table not match my list.
One way is like this:
SELECT DS.*
FROM
(
VALUES (354469477)
,(354469536)
,(354469500)
,(987359)
) DS (bold_id)
LEFT JOIN TripEvent TE
ON DS.[bold_id] = TE.[bold_id]
WHERE TE.[bold_id] IS NULL;
Of course, there are diff ways to populated the DS. I will recommend to populate the search IDs in a temporary table.
Since you are using a SQL Server DB, you can use EXCEPT:
SELECT bold_id FROM
(
SELECT 354469477 AS bold_id
UNION ALL
SELECT 354469536
UNION ALL
SELECT 354469500
UNION ALL
SELECT 987359
) listofValues
EXCEPT
SELECT bold_id
FROM TripEvent;
OR:
SELECT bold_id FROM
(
VALUES (354469477),
(354469536),
(354469500),
(987359)
) listofValues(bold_id)
EXCEPT
SELECT bold_id
FROM TripEvent;
Have a look in the documentation
Found this query on another places. Seems to work. Thanks for the good response :)
SELECT *
from (values (354469477),(354469536),(354469500),(987359)) as v(id)
WHERE NOT EXISTS (SELECT BOLD_ID FROM TripEvent WHERE TripEvent.BOLD_ID = v.id)

Count instances of value (say, '4') in several columns/ rows

I have survey responses in a SQL database. Scores are 1-5.
Current format of the data table is this:
Survey_id, Question_1, Question_2, Question_3
383838, 1,1,1
392384, 1,5,4
393894, 4,3,5
I'm running a new query where I need % 4's, % 5's ... question doesn't matter, just overall.
At first glance I'm thinking
sum(iif(Question_1 =5,1,0)) + sum(iif(Question_2=5,1,0)) .... as total5s
sum(iif(Question_1=4,1,0)) + sum(iif(Question_2=4,1,0)) .... as total4s
But I am unsure if this is the quickest or most elegant way to achieve this.
EDIT: Hmm on first test this query already appears not to work correctly
EDIT2: I think I need sum instead of count in my example, will edit.
You have to unpivot the data and calculate the % responses thereafter. Because there are a limited number of questions, you can use union all to unpivot the data.
select 100.0*count(case when question=4 then 1 end)/count(*) as pct_4s
from (select survey_id,question_1 as question from tablename
union all
select survey_id,question_2 from tablename
union all
select survey_id,question_3 from tablename
) responses
Another way to do this could be
select 100.0*(count(case when question_1=4 then 1 end)
+count(case when question_2=4 then 1 end)
+count(case when question_3=4 then 1 end))
/(3*count(*))
from tablename
With unpivot as #Dudu suggested,
with unpivoted as (select *
from tablename
unpivot (response for question in (question_1,question_2,question_3)) u
)
select 100.0*count(case when response=4 then 1 end)/count(*)
from unpivoted

Loop inside SELECT in SQL Server query

I'm trying to get a few columns using a function F(a,b,x) from SQL Server for say 20 values. Basically it's like
SELECT
col1, col2, F(a,b,1), F(a,b,2), ... F(a,b,20)
FROM
table
Is that possible to use a loop to SELECT F(a,b,#i) where 0 < #i < 21?
Thanks!
There is no "looping" in a SQL statement. But, you can come close by doing:
select col1, col2, n.n, f(a, b, n.n)
from table t cross join
(select 1 as n union all select 2 union all select 3 . . .
select 20
) n;
The exact syntax depends on the database you are using. There are also ways to generate numbers, once again, depending on the database.
This generates 20 rows for each row in the table, rather than 20 columns. The results can be pivoted if you really need them in columns.

How to form an SQL query that is like using 'IN' across two columns as pairs?

I have two integer columns and I wish to select rows with particular pairings of values. What SQL syntax can I use? For example, using IN it might look something like this if IN supported this syntax:
select *
from myTable
where value1, value2 in ((2,3), (3,4), (2,5), (3,6))
To select those rows with
value1 == 2 and value2 == 3 or value1==3 and value2==4 or 2/5 or 3/6.
I'm using a proprietary SQL system, so basic SQL is preferred. Or if there is none, having a statement that works in some standard SQL would be useful as well.
select yourtable.*
from yourtable
inner join
(
select 2 as v1, 3 as v2
union select 3,4
union select 2,5
union select 3,6
) pairs
on yourtable.value1 = pairs.v1
and yourtable.value2 = pairs.v2
Well in SQL Server you can't use IN that way unfortunately. I think your best bet is going to be to write it out like you did below your code sample or to load your data into a CTE or something and then joining on that.
same can be achieved by using VALUES
select table_name.*
from table_name tn,
(values(2,3), (3,4), (2,5), (3,6) ) as val(v1,v2)
where tn.value1 = val.v1 and tn.value2 = val.v2

SQL First Match or just First

Because this is part of a larger SQL SELECT statement, I want a wholly SQL query that selects the first item matching a criteria or, if no items match the criteria, just the first item.
I.e. using Linq I want:
Dim t1 = From t In Tt
Dim t2 = From t In t1 Where Criteria(t)
Dim firstIfAny = From t In If(t2.Any, t2, t1) Take 1 Select t
Because If is not part of Linq, LinqPad doesn't show a single SQL statement, but two, the second depending upon whether the Criteria matches any of the Tt values.
I know it will be SELECT TOP 1 etc. and I can add ORDER BY clauses to get the specific first one I want, but I'm having trouble thinking of the most straightforward way to get the first of two criteria. (It was at exactly this point when I was able to solve this myself.)
Seeing as I don't see an existing question for this, I will let it stand. I'm sure someone else will see the answer quickly.
select top 1 *
from (
select top 1 *, 1 as Rank from MyTable where SomeColumn = MyCriteria
union all
select top 1 *, 2 as Rank from MyTable order by MyOrderColumn
) a
order by Rank
I've gone with this:
SELECT TOP 1 *
FROM MyTable
WHERE SomeColumn = MyCriteria
OR NOT (EXISTS (SELECT NULL FROM MyTable WHERE SomeColumn = MyCriteria))
ORDER BY MyOrdering
My actual SomeColumn = MyCriteria is rather more complex of course, as well as other unrelated where clauses.