Select ids from array of numbers that are not in a Table - sql

Given the following table data:
Students.id
-----------
1
2
3
If I want to select ids from a table that are not in an array of numbers, I can do the following:
SELECT id FROM Students
WHERE id NOT IN (1, 3, 5);
Result: (2)
But I want to do the opposite - select numbers from an array that are not in a table. How can I do that?
Here have been my attempts so far:
Attempt 1:
SELECT id FROM TABLE(1, 3, 5)
WHERE id NOT IN (SELECT id FROM Students);
RESULT: ORA-00907: missing right parenthesis
------------------------
Attempt 2:
SELECT (1, 3, 5) FROM dual
WHERE ??? NOT IN (SELECT id from Students); -- not sure what the column name should be
RESULT: Executing the first line alone gives the error ORA-00907: missing right parenthesis
--------------
Attempt 3:
SELECT TABLE(1,3,5) AS ids FROM dual
WHERE ids NOT IN (SELECT id FROM Students);
RESULT: ORA-00936: missing expression
Desired results (from my examples): (5)
Note: I found this related question, but alas it is also unanswered. Like Fuzz, my array of numbers are not stored in a table.

You can create a collection type:
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TYPE NumbersList IS TABLE OF NUMBER;
and then you can use it in a query:
Query 1:
SELECT id
FROM students
WHERE id NOT MEMBER OF NumbersList( 1, 3, 5 )
Results:
| ID |
|----|
| 2 |
Or, to reverse it and have the numbers in the collection not in the table, you can use a table collection expression (TABLE(:your_collection)):
Query 2:
SELECT COLUMN_VALUE
FROM TABLE( NumbersList( 1, 3, 5 ) )
WHERE COLUMN_VALUE NOT IN ( SELECT id FROM students )
Results:
| COLUMN_VALUE |
|--------------|
| 5 |
You can even pass the collection as a bind variable (examples passing it from PL/SQL and from a java array).

You could use the table build in proper way eg: using union instead of an array :
select id from (
select 1 id from dual
union
select 3 from dual
union
select 5 from dual ) t
where t.id NOT IN (
SELECT id FROM Student
)

You may try anti joining a CTE containing your ID numbers to the Students table:
WITH cte AS (
SELECT 1 AS id FROM dual UNION ALL
SELECT 3 FROM dual UNION ALL
SELECT 5 FROM dual
)
SELECT t1.id
FROM cte t1
LEFT JOIN Students t2
ON t1.id = t2.id
WHERE t2.id IS NULL
Your requirement is fairly easy to come by, using a join or a few other methods, provided that your data is in a proper table. If you have a long term need for this, then I recommend getting your data into tables.
We could also write the above query using EXISTS, which might have better performance:
SELECT t1.id
FROM cte t1
WHERE NOT EXISTS (SELECT 1 FROM Students t2 WHERE t1.id = t2.id)

Related

SQLite - Return Rows Even If They Are Duplicates

I have a simple SQLite table which has just one ID column.
I have some variable IDs that may be duplicates of each other like: 1,2,3,4,3,1 (These IDs are just examples, there could be hundreds of them).
And I have a simple query as follows:
SELECT ID FROM TABLE WHERE ID in (1,2,3,4,3,1)
In the usual case the answer contains only 4 rows with ids 1,2,3,4. Is there any way to force SQLite to return rows in the order of the request (1,2,3,4,3,1) even if they are duplicates?
I have n IDs in my query and I want n rows in return even if they are duplicates.
Edit: The Table Definition is:
CREATE TABLE TEST(ID TEXT PRIMARY KEY)
You can use left join:
select t.*
from (select 1 as id, 1 as ord union all
select 2 as id, 2 as ord union all
select 3 as id, 3 as ord union all
select 4 as id, 4 as ord union all
select 3 as id, 5 as ord union all
select 1 as id, 6 as ord
) ids left join
t
on t.id = ids.id
order by ids.ord;

Cross join returning row from only one table

I'm trying to get two different columns by cross joining on same table but getting only on e column. Following is the sample query :
select 1 from dual cross join (select 2 from dual) t1;
Expected Result : 1 2
but getting only 1.
You have the select clause of
select 1
where you select a single column. If you want an output of 1 2 then use
select 1, 2
as your select clause.
You are not retrieving data from t1
select 1 as id, t1.*
from dual cross join (select 2 id1 from dual) t1;

ORACLE join two table with comma separated ids

I have two tables
Table 1
ID NAME
1 Person1
2 Person2
3 Person3
Table 2
ID GROUP_ID
1 1
2 2,3
The IDs in all the columns above refer to the same ID (Example - a Department)
My Expected output (by joining both the tables)
GROUP_ID NAME
1 Person1
2,3 Person2,Person3
Is there a query with which I can achieve this.
It can be done. You shouldn't do it, but perhaps you don't have the power to change the world. (If you have a say in it, you should normalize your table design - in your case, both the input and the output fail the first normal form).
Answering more as good practice for myself... This solution guarantees that the names will be listed in the same order as the id's. It is not the most efficient, and it doesn't deal with id's in the list that are not found in the first table (it simply discards them instead of leaving a marker of some sort).
with
table_1 ( id, name ) as (
select 1, 'Person1' from dual union all
select 2, 'Person2' from dual union all
select 3, 'Person3' from dual
),
table_2 ( id, group_id ) as (
select 1, '1' from dual union all
select 2, '2,3' from dual
),
prep ( id, lvl, token ) as (
select id, level, regexp_substr(group_id, '[^,]', 1, level)
from table_2
connect by level <= regexp_count(group_id, ',') + 1
and prior id = id
and prior sys_guid() is not null
)
select p.id, listagg(t1.name, ',') within group (order by p.lvl) as group_names
from table_1 t1 inner join prep p on t1.id = p.token
group by p.id;
ID GROUP_NAMES
---- --------------------
1 Person1
2 Person2,Person3
select t2.group_id, listagg(t1.name,',') WITHIN GROUP (ORDER BY 1)
from table2 t2, table1 t1
where ','||t2.group_id||',' like '%,'||t1.id||',%'
group by t2.id, t2.group_id
Normalize you data model, this perversion !!! Сomma separated list should not exist in database. Only individual rows per data unit.

how to repeat each row twice

I have a requirement for a report and I would like my sql query to repeat each row twice.
Example :
**Table 1**
Id Name
1 Ab
2 Cd
3 Ef
I want to write a query which outputs the following :
1 Ab
1 Ab
2 Cd
2 Cd
3 Ef
3 Ef
Is there a way I can do it ?
I cannot think of anything except using union
Select Id, name from Table1 union select Id, name from Table1
You can use a union all. A union will not work, because it will eliminate duplicates. Another way is a cross join:
select id, name
from table1 t1 cross join
(select 1 as n union all select 2) n;
You can also use UNION ALL, put them under CTE (Common Table Expression) and Order By Id:
WITH CTE AS
(
SELECT Id, Name FROM Table_1
UNION ALL
SELECT Id, Name FROM Table_1
)
SELECT Id, Name
FROM CTE
ORDER BY Id;
As this will reorder them and stacked them as duplicates
Solution will be like this:
select Id, name from Table1
union all
select Id, name from Table1

How to add 2 temporary tables together

If I am creating temporary tables, that have 2 columns. id and score. I want to to add them together.
The way I want to add them is if they each contain the same id then I do not want to duplicate the id but instead add the scores together.
if I have 2 temp tables called t1 and t2
and t1 had:
id 3 score 4
id 6 score 7
and t2 had:
id 3 score 5
id 5 score 2
I would end up with a new temp table containing:
id 3 score 9
id 5 score 2
id 6 score 7
The reason I want to do this is, I am trying to build a product search. I have a few algorithms I want to use, 1 using fulltext another not. And I want to use both algorithms so I want to create a temporary table based on algorithm1 and a temp table based on algorithm2. Then combine them.
How about:
SELECT id, SUM(score) AS score FROM (
SELECT id, score FROM t1
UNION ALL
SELECT id, score FROM t2
) t3
GROUP BY id
This is untested but you should be able to perform a union on the two tables and then perform a select on the results, grouping the fields and adding the scores
SELECT id,SUM(score) FROM
(
SELECT id,score FROM t1
UNION ALL
SELECT id,score FROM t2
) joined
GROUP BY id
Perform a full outer join on the ID. Select on the ID and the sum of the two "score" columns after coalescing the values to 0.
SELECT id, SUM(score) FROM
(
SELECT id, score FROM #t1
UNION ALL
SELECT id, score FROM #t2
) AS Temp
GROUP BY id
select id, sum(score)
from (
select * from table 1
union all
select * from table2
) tables
group by id
You need to create an union of those two tables then You can easily group the results.
SELECT id, sum(score) FROM
(
SELECT id, score FROM t1
UNION
SELECT id, score FROM t2
) as tmp
GROUP BY id;