Returning not found values in query - sql

I'm working on a project, and I have a list of several thousand records that I need check. I need to provide the list of the ones NOT found, and provide the query I use to locate them so my superiors can check their work.
I'll admit I'm relatively new to SQL. I don't have access to create temporary tables, which is one way I had thought of to do this.
A basic idea of what I'm doing:
select t.column1, t.column2
from table1 t
where t.column1 in ('value1','value2','value3')
If value1 and value3 are in the database, but value2 is not, I need to display value2 and not the others.
I have tried ISNULL, embedding the query, and trying to select NOT values from the query.
I have searched for returning records not found in a query on Google and on this site, and still found nothing.

I have tried something similar:
First create a table which will contain all such values that you need.
lets say
create table table_values(values varchar2(30));
then try the in clause as below:
select * from table_values tv where tv.value not in (select t.column1
from table1 t);
this will return the values needed.

In SQL Server 2008, you can make derived tables using the syntax VALUES(...)(...)(...), e.g.
select v.value
from (
values ('value1'),('value2'),('value3')
) v(value)
left join table1 t on t.column1 = v.value
where t1.column1 is null
Notes:
Each (...) after VALUES is a single row, and you can have multiple columns.
The v(value) after the derived table is the alias given to the table, and column name(s).
LEFT JOIN keeps the values from the derived table v even when the record doesn't exist in table1
Then, we keep only the records that cannot be matched, i.e. t1.column1 is null
I've switched the first column in your select to the column from v. column2 is removed because it is always null

solution might work in Oracle where dual is single row single column table. You need
one table where you can make virtual select of desired values!
WARNING as I don't have access to db I never tested query below.
SELECT tab_all.col_search, t.column1, t.column2
FROM
(
Select value1 AS col_search from dual
union all
Select value2 from dual
union all
Select value3 from dual
) tab_all left join table1 t
on col_search = t.column1
WHERE t.column1 is null;
I belive sqlserver equivalent of Oracle's
SELECT value1 FROM dual is
SELECT value1 OR SELECT 'value1'.
So try
SELECT tab_all.col_search, t.column1, t.column2
FROM
(
Select value1 AS col_search
union all
Select value2 AS col_search
union all
Select value3 AS col_search
) tab_all left join table1 t
on col_search = t.column1
WHERE t.column1 is null;
As I am not sqlserver person might be that
RichardTheKiwi version of Oracle's select from dual is better.

Related

Create a view of a table with a column that has multiple values

I have a table (Table1) like the following:
Col1
Col2
First
Code1,Code2,Code3
Second
Code2
So Col2 can contain multiple values comma separated, I have another table (Table2) that contains this:
ColA
ColB
Code1
Value1
Code2
Vaue2
Code3
Vaue3
I need to create a view that joins the two tables (Table1 and Table2) and returns something like this:
Col1
Col2
First
Value1,Value2,Value3
Second
Value2
Is that possible? (I'm on Oracle DB if that helps.)
It's a violation of first normal form to have a list in a column value like that. It causes a lot of difficulties in a relational database, like the one you are encountering now.
However, you can get what you want by using the LIKE operator to find colA values that are substrings of the Col2 column. Add delimiters before and after to catch the first and last ones. Then aggregate back up to a single list using LISTAGG.
SELECT table1.col1,
LISTAGG(table2.colB,',') WITHIN GROUP (ORDER BY table2.colB) value_list
FROM table1,
table2
WHERE ','||table1.col2||',' LIKE '%,'||table2.colA||',%'
GROUP BY table1.col1
This will not perform well on large volumes, because without an equijoin it's going to use nested loops, and you can't use an index on a LIKE predicate with % at the beginning. The combination of nested loops + FTS is not pleasant with large volumes of data. Therefore, if this is your situation, you will need to fix the 1NF problem by transforming table1 into normal relational format, and then join it to table2 with an equijoin, which will enable it to use a hash join instead. So:
SELECT table1.col1,
LISTAGG(table2.colB,',') WITHIN GROUP (ORDER BY table2.colB) value_list
FROM (SELECT t.col1,
SUBSTR(t.col2,INSTR(t.col2,',',1,seq)+1,INSTR(t.col2,',',1,seq+1)-(INSTR(t.col2,',',1,seq)+1)) col2_piece
FROM (SELECT col1,
','||col2||',' col2
FROM table1) t,
(SELECT ROWNUM seq FROM dual CONNECT BY LEVEL < 10) x) table1,
table2
WHERE table1.col2_piece IS NOT NULL
AND table1.col2_piece = table2.colA
GROUP BY table1.col1
If you want the values in the same order in the list as the terms then you can use:
SELECT t1.col1,
LISTAGG(t2.colb, ',') WITHIN GROUP (
ORDER BY INSTR(','||t1.col2||',', ','||t2.colA||',')
) AS value2
FROM table1 t1
INNER JOIN table2 t2
ON INSTR(','||t1.col2||',', ','||t2.colA||',') > 0
GROUP BY
t1.col1
Which, for the sample data:
CREATE TABLE Table1 (Col1, Col2) AS
SELECT 'First', 'Code1,Code2,Code3' FROM DUAL UNION ALL
SELECT 'Second', 'Code2' FROM DUAL;
CREATE TABLE Table2 (ColA, ColB) AS
SELECT 'Code1', 'XXXX' FROM DUAL UNION ALL
SELECT 'Code2', 'ZZZZ' FROM DUAL UNION ALL
SELECT 'Code3', 'YYYY' FROM DUAL;
Outputs:
COL1
VALUE2
First
XXXX,ZZZZ,YYYY
Second
ZZZZ
fiddle

How to get also the not existing values

I've got a query like this
select column, count(*)
from mytable
where column in ('XXX','YYY','ZZZ',....)
group by column;
But I want also to get a row for values the aren't in the table.
Let's suppose that 'ZZZ' doesn't exist in mytable, I'd like to get:
COLUMN COUNT(*)
XXX 3
YYY 2
ZZZ 0 (or NULL)
Oracle version 10g
Thanks in advance
Mark
In general, you would need to have a second table which contains all the possible column values whose counts you want to appear in the output. For demo purposes only, we can use a CTE for that:
WITH vals AS (
SELECT 'XXX' AS val UNION ALL
SELECT 'YYY' UNION ALL
SELECT 'ZZZ'
)
SELECT t1.val, COUNT(t2.col) AS cnt
FROM vals t1
LEFT JOIN mytable t2
ON t2.col = t1.val
GROUP BY
t1.val;

SQL Server: Find Values that don't exist in a table

I have a list or set of values that I would like to know which ones do not currently exist in a table. I know I can find out which ones do exist with:
SELECT * FROM Table WHERE column1 IN (x,x,x,x,x)
The set is the values I am checking against. Is there a way to find out which values in that set do not exist in column1? Basically, I'm looking for the inverse of the sql statement above.
This is for a report, so all I need is the values that don't exist to be returned back.
I have and could do this with a left join and putting the values in another table, but the values I check are always different and was hoping to find a solution that didn't involve clearing a table and inserting data first. Trying to find a better solution for me if one exists.
You can also use EXCEPT as well as the OUTER JOIN e.g.
SELECT * FROM
(
SELECT -1 AS N
UNION
SELECT 2 AS N
) demo
EXCEPT
SELECT number
FROM spt_values
WITH q(x) AS
(
SELECT x1
UNION ALL
SELECT x2
UNION ALL
SELECT x3
)
SELECT x
FROM q
WHERE x NOT IN
(
SELECT column1
FROM [table]
)
Put the values you want to check for in a table A
LEFT OUTER JOIN the table A against your Table WHERE Table.column1 IS NULL
SELECT column1
FROM A
LEFT OUTER JOIN
Table
ON A.column1 = Table.column1
WHERE Table.column1 IS NULL
This will only show the rows that exist in A but not in Table.
As you want some of the values from the set in the result, and you can't take them from the table (as you want the ones that doesn't exist there), you have to put the set in some kind of table or result so that you can use that as source.
You can for example make a temporary result, that you can join against the table to filter out the ones that does exist in the table:
select set.x
from (
select 1 as x union all
select 2 union all
select 3 union all
select 4 union all
select 5
) as set
left join Table as t on t.column1 = set.x
where t.columnn1 is null
One way you can do it is:
SELECT * FROM Table WHERE column1 NOT IN(...);
Use the NOT operator:
SELECT * FROM Table WHERE column1 NOT IN (x,x,x,x,x)

Single SQL SELECT Returning multiple rows from one table row

We have a table which is of the form:
ID,Value1,Value2,Value3
1,2,3,4
We need to transform this into.
ID,Name,Value
1,'Value1',2
1,'Value2',3
1,'Value3',4
Is there a clever way of doing this in one SELECT statement (i.e without UNIONs)? The column names Value1,Value2 and Value3 are fixed and constant.
The database is oracle 9i.
Give a union a shot.
select ID, 'Value1' as Name, Value1 as Value from table_name union all
select ID, 'Value2', Value2 as Value from table_name union all
select ID, 'Value3', Value3 as Value from table_name
order by ID, Name
using union all means that the server won't perform a distinct (which is implicit in union operations). It shouldn't make any difference with the data (since your ID's should HOPEFULLY be different), but it might speed it up a bit.
This works on Oracle 10g:
select id, 'Value' || n as name,
case n when 1 then value1 when 2 then value2 when 3 then value3 end as value
from (select rownum n
from (select 1 from dual connect by level <= 3)) ofs, t
I think Oracle 9i had recursive queries? Anyway, I'm pretty sure it has CASE support, so even if it doesn't have recursive queries, you can just do "(select 1 from dual union all select 2 from dual union all select 3 from dual) ofs" instead. Abusing recursive queries is a bit more general- for Oracle. (Using unions to generate rows is portable to other DBs, though)
You can do it like this, but it's not pretty:
SELECT id,'Value 1' AS name,value1 AS value FROM mytable
UNION
SELECT id,'Value 2' AS name,value2 AS value FROM mytable
UNION
SELECT id,'Value 3' AS name,value3 AS value FROM mytable
Unioning three select statements should do the trick:
SELECT ID, 'Value1', Value1 AS Value
FROM TABLE
UNION
SELECT ID, 'Value2', Value2 AS Value
FROM TABLE
UNION
SELECT ID, 'Value3', Value3 AS Value
FROM TABLE
If you're using SQL Server 2005+ then you can use UNPIVOT
CREATE TABLE #tmp ( ID int, Value1 int, Value2 int, Value3 int)
INSERT INTO #tmp (ID, Value1, Value2, Value3) VALUES (1, 2, 3, 4)
SELECT
*
FROM
#tmp
SELECT
*
FROM
#tmp
UNPIVOT
(
[Value] FOR [Name] IN (Value1, Value2, Value3)
) uPIVOT
DROP TABLE #tmp
A UNION ALL, as others have suggested, is probably your best bet in SQL. You might also want to consider handling this in the front end depending on what your specific requirements are.
CTE syntax may be different for Oracle (I ran it in Teradata), but I only used CTE to provide test data, those 1 2 3 and 4. You can use temp table instead. The actual select statement is plain vanilla SQL and it will on any relational database.
For Sql Server, consider UNPIVOT as an alternative to UNION:
SELECT id, value, colname
FROM #temp t
UNPIVOT (Value FOR ColName IN (value1,value2,value3)) as X
This will return the column name as well. I'm unsure what the X is used for, but you can't leave it out.
Try this:
CTE creates a temp table with 4 values. You can run this as is in any database.
with TEST_CTE (ID) as
(select * from (select '1' as a) as aa union all
select * from (select '2' as b) as bb union all
select * from (select '3' as c) as cc union all
select * from (select '4' as d) as dd )
select a.ID, 'Value'|| a.ID, b.ID
from TEST_CTE a, TEST_CTE b
where b.ID = (select min(c.ID) from TEST_CTE c where c.ID > a.ID)
Here is the result set:
1 Value1 2
2 Value2 3
3 Value3 4
Enjoy!
Some afterthoughts.
^^^ CTE syntax may be different in Oracle. I could only run it in Teradata. You can substitute it with temp table or fix the syntax to make it Oracle compatible. The select statement is plain vanilla SQL that will work on any database.
^^^ Another thing to note. If ID field is numeric, you might need to cast it into CHAR in order to concatenate it with "Value".

Column names for a table formed by a UNION

Given a couple of simple tables like so:
create table R(foo text);
create table S(bar text);
If I were to union them together in a query, what do I call the column?
select T.????
from (
select foo
from R
union
select bar
from S) as T;
Now, in mysql, I can apparently refer to the column of T as 'foo' -- the name of the matching column for the first relation in the union. In sqlite3, however, that doesn't seem to work. Is there a way to do it that's standard across all SQL implementations?
If not, how about just for sqlite3?
Correction: sqlite3 does allow you to refer to T's column as 'foo' after all! Oops!
Try to give an alias to columns;
select T.Col1
from (
select foo as Col1
from R
union
select bar as Col1
from S) as T;
or If the name of column is not necessary then T.* will be enough.
Although there is no spelled rule, we can use the column names from the first subquery in the union query to fetch the union results.
you only need column aliases only in first select (tested in SQl Server 2008 R2)
select T.Col1
from (
select 'val1' as Col1
union
select 'val2'
union
select 'val3'
) as T;