Show elements of where clause that are not present in table - sql

I search a table based on an ID column in my where clause. I have a list of IDs that may or may not be present in this table. A simple query will give me the IDs which exist in that table (if any). Is there a way to also return ID's that were not found ?
Table --
ID
1GH
2BN
3ER
SELECT *
FROM Table
WHERE ID IN (big list 9FG, 1GH, 3UI etc)
--If ID's in above list are not in table, then show those ids.
Desired output -
9FG, 3UI were not found in the table

If I understand correctly what you need you can do it this way
SELECT q.id,
CASE WHEN t.id IS NULL THEN 'no' ELSE 'yes' END id_exists
FROM
(
SELECT '9FG' id UNION ALL
SELECT '1GH' UNION ALL
SELECT '3UI'
) q LEFT JOIN table1 t
ON q.id = t.id
Output:
| ID | ID_EXISTS |
|-----|-----------|
| 9FG | no |
| 1GH | yes |
| 3UI | no |
or if you just need a list of non-existent ids
SELECT q.id
FROM
(
SELECT '9FG' id UNION ALL
SELECT '1GH' UNION ALL
SELECT '3UI'
) q LEFT JOIN table1 t
ON q.id = t.id
WHERE t.id IS NULL
Output:
| ID |
|-----|
| 9FG |
| 3UI |
The trick is to use an OUTER JOIN instead of WHERE condition to filter data from your table and be able to see the mismatches.
Here is SQLFiddle demo

To search you can use
SELECT *
From Mytable
where id in (
select id from (values (1), (2), (3)) as SearchedIds(Id) )
and the opposite to find unamtched:
SELECT id from (values (1), (2), (3)) as SearchedIds(Id)
WHERE id not in (SELECT id From MyTable)
The syntax
Values(...) asSearchedIds(id)
is supported in Sql2008, for Sql2005 you have to do
( SELECT 1 as Id UNION ALL SELECT 2 UNION ALL ...etc ) as SearchedIds
Note: you can rewrite those queries with JOINS (INNER and LEFT)

Maybe something like:
SELECT id FROM my_table WHERE id NOT IN (val1, val2, val3)

Related

Merging two tables with no unique keys without row number

I have two tables
Table A:
Name
----
Andy
Greg
Table B:
Value
-----
1
2
I want to merge these two tables into one:
Table C:
Result
------
Andy 1
Greg 2
Note:-
without changing the order. I cannot use row numbers as I am using Apache Calcite and it doesn't support that right now.
Is this possible?
WITH X AS
(
SELECT * FROM
(
SELECT NAME AS Val1,
(SELECT Count(*) from #TableA a1 WHERE a1.Name < a2.Name) AS myRowNumber1 FROM #TableA a2
)a1
INNER JOIN
(
SELECT Id AS Val2,
(SELECT Count(*) from #TableB a1 WHERE a1.Id < a2.Id) AS myRowNumber2 FROM #TableB a2
)b1
ON a1.myRowNumber1=b1.myRowNumber2
)
SELECT Val1 +' '+ Val2 AS Result FROM X
You can use Count(*) instead of Row_Number()
OutPut:-
Result
---------
Andy 1
Greg 2
Create a new column as an identifier
SELECT *, ROW_NUMBER() OVER(PARTITION BY NAME ORDER BY NAME) AS id
INTO #NAMES
FROM TABLE1
SELECT *, ROW_NUMBER() OVER(PARTITION BY VALUE ORDER BY VALUE) AS id
INTO #VALUES_TABLE
FROM TABLE2
And then join by the row number that will be called id
SELECT *
FROM #NAMES t1
LEFT JOIN #VALUES_TABLE t2
ON t1.id = t2.id
Calcite does not have a function exactly like Oracle's ROWNUM pseudocolumn but it does have the standard SQL window function ROW_NUMBER(). You can use it as follows:
create table a as select * from (values ('Andy'), ('Greg')) as t (name);
create table b as select * from (values (1), (2)) as t (v);
select *
from (select name, row_number() over () as id from a)
join (select v, row_number() over () as id from b)
using (id);
+----+------+---+
| ID | NAME | V |
+----+------+---+
| 1 | Andy | 1 |
| 2 | Greg | 2 |
+----+------+---+
(2 rows)
If you want deterministic order, you can change over () to, say, over (order by name desc).

Select data in tables by conditions between 2 tables which is not linked

I have a large select with a lot of inner joins.
In the select I have an array_agg function for one set of data.
This array contains only a column of a table, but now I want to append at the end of the array data from another table. The data I need to add is not directly linked with the previous table where I need the column.
Query example:
select
origin_table.x,
origin_table.y,
array_agg(table1.data) ...
from
origin_table
inner join ... inner join ... full join table1 on
table.origin_table_id = origin_table.id ...
group by
...
Result array:
ID 1: example_data, {baba, bobo}
ID 2: example_data, {bibi, bubu}
Example of my tables:
table 1:
id | data | origin_table_id
----+---------+----------
1 | baba | 1
2 | bobo | 1
3 | bibi | 2
4 | bubu | 2
table 2:
id | data_bis
---+---------
1 | byby
2 | bebe
origin table:
id | table2_id
---+----------
1 | 2
2 | 1
Expected result with the 3 tables:
ID 1: example_data, {baba, bobo, bebe}
ID 2: example_data, {bibi, bubu, byby}
But got :
ID 1: example_data, {baba, bobo, bebe, byby}
ID 2: example_data, {bibi, bubu, bebe, byby}
What I need is:
How to have all the data of table 1 which respect the condition and append to it the unique table 2 data but not all elements of the table.
Try below query..
create table tab1 (id integer,data character varying,origin_table_id integer);
insert into tab1
select 1,'baba',1
union all
select 2,'bobo',1
union all
select 3,'bibi',2
union all
select 4,'bubu',2
create table tab2 (id integer,data_bis character varying);
insert into tab2
select 1,'byby'
union all
select 2,'bebe'
create table OriginalTable (id integer,table2_id integer);
insert into OriginalTable
select 1,2
union all
select 2,1
select * from OriginalTable
select origin_table_id,data
from tab1
union all
select OriginalTable.table2_id,data_bis
from OriginalTable
join tab2 on tab2.id = OriginalTable.id
order by origin_table_id
Result:
1;"baba"
1;"bobo"
1;"bebe"
2;"bibi"
2;"bubu"
2;"byby"
I can give you some idea and sample code to achieve your requirement.
First you can UNION ALL table 'table 1' and 'table 2' using the relation table 'origin table'.
WITH CTE
AS
(
SELECT AA.id,AA.data_bis,AA.origin_table_id
FROM table_1 AA
UNION ALL
SELECT NULL id, A.data_bis,B.id origin_table_id
FROM table_2 A
INNER JOIN origin_table B
ON A.id = B.table2_id
)
SELECT * FROM CTE
After applying UNION ALL, data will be looks like below-
data_bis origin_table_id
baba 1
bobo 1
bibi 2
bubu 2
byby 2
bebe 1
Now you can apply 'string_agg' on your data as below-
SELECT origin_table_id,string_agg(DISTINCT data_bis,',')
FROM CTE
GROUP BY origin_table_id
And the output will be-
origin_table_id string_agg
1 baba,bebe,bobo
2 bibi,bubu,byby
Now, you can apply further JOINING to this data as per your requirement. You can check DEMO HERE
Please keep in mind hat this is not exact solution of your issue. Just idea...

Select item that is different in each of group by a column

I have this sample table
+--------+-------------+
| DBName | Description |
+--------+-------------+
| A | Car |
| A | Boat |
| B | Car |
| B | Plane |
| C | Car |
| C | Boat |
| C | Plane |
+--------+-------------+
I want to take only Description that is not exist on every DBName and show what DBName that don't have the Description.
The Result from the query that I want
+--------+-------------+
| DBName | Description |
+--------+-------------+
| A | Plane |
| B | Boat |
+--------+-------------+
Keep in mind it will be more than just A,B,C on DBName.
Interesting issue. Here are a couple of options for solving. There's discussions around these techniques here, along with a few suggestions of other routes for handling scenarios such as this.
SQL Fiddle Example
select DBName, Description
from (
select DBName, Description
from (select distinct DBName from demo) a
cross join (select distinct Description from demo) b
) c
except
select DbName, Description from demo
This solution works by fetching every possible combination (via cross join of distinct values for each column), then excluding all those which already exist via the except clause.
SQL Fiddle Example
select [each].DBName, missing.Description
from (select distinct DBName from demo) [each]
cross join (select distinct Description from demo) [missing]
where not exists
(
select top 1 1
from demo [exists]
where [exists].DbName = [each].DBName
and [exists].Description = [missing].Description
)
This solution is the same as the above, only instead of the except cluase we use where not exists to remove existing combos.
Ideally you should have a master list of data. In case you do not you should deriv3 it from the data and then put checks against them like below:
SQL Fiddle Example
select
masterlistDbname.Dbname,
masterlistDesc.Description
from
(
select distinct Description from yourtable
) masterlistDesc
cross join
(
select distinct Dbname from yourtable
) masterlistDbname
left join
yourtable t1
on t1.Dbname = masterlistDbname.Dbname
and t1.Description = masterlistDesc.Description
where t1.Dbname is NULL
use NOT EXISTS
SELECT *
FROM yourtable t
WHERE NOT EXISTS
(
SELECT *
FROM yourtable x
WHERE x.Description = t.Description
AND x.DBName <> t.DBName
)
you should throw little more Sample data.
Try this,
create table #test(DBName varchar(50),Descriptions varchar(50) )
insert into #test VALUES
('A','Car')
,('A','Boat')
,('B','Car')
,('B','Plane')
,('C','Car')
,('C','Boat')
,('C','Plane')
;
WITH CTE
AS (
SELECT *
,ROW_NUMBER() OVER (
ORDER BY (
SELECT NULL
)
) rn
,ROW_NUMBER() OVER (
PARTITION BY DBName ORDER BY (
SELECT NULL
)
) rn1
FROM #test
)
SELECT t.DBName
,t1.Descriptions
FROM cte t
CROSS APPLY (
SELECT TOP 1 Descriptions
FROM cte t1
WHERE t1.rn > t.rn
AND t.Descriptions <> t1.Descriptions
AND t.dbname <> t1.dbname
ORDER BY t1.rn
) t1
WHERE t.rn1 = 1
drop table #test

Two rows with the same id and two different values, getting the second value into another column

I have two rows with the same id but different values. I want a query to get the second value and display it in the first row.
There are only two rows for each productId and 2 different values.
I've tried looking for this for the solution everywhere.
What I have, example:
+-----+-------+
| ID | Value |
+-----+-------+
| 123 | 1 |
| 123 | 2 |
+-----+-------+
What I want
+------+-------+---------+
| ID | Value | Value 1 |
+------+-------+---------+
| 123 | 1 | 2 |
+------+-------+---------+
Not sure whether order matters to you. Here is one way:
SELECT MIN(Value), MAX(Value), ID
FROM Table
GROUP BY ID;
This is a self-join:
SELECT a.ID, a.Value, b.Value
FROM table a
JOIN table b on a.ID = b.ID
and a.Value <> b.Value
You can use a LEFT JOIN instead if there are IDs that only have one value and would be lost by the above JOIN
May be you may try this
DECLARE #T TABLE
(
Id INT,
Val INT
)
INSERT INTO #T
VALUES(123,1),(123,2),
(456,1),(789,1),(789,2)
;WITH CTE
AS
(
SELECT
RN = ROW_NUMBER() OVER(PARTITION BY Id ORDER BY Val),
*
FROM #T
)
SELECT
*
FROM CTE
PIVOT
(
MAX(Val)
FOR
RN IN
(
[1],[2]--Add More Numbers here if there are more values
)
)Q

SQL - Select In from Array, and NOT in in same query

I have a userform built in VBA where my coworkers can enter multiple values that builds an array and places it into an IN statement, which works great. Problem is I need to also be able to display what values do not exist within the tables.
Example table
id | value
1 | value1
2 | value2
4 | value4
Then a query that could be generated would be
SELECT [id],[value] FROM [tablea] WHERE [id] IN (1,2,3,4)
Expected or desirable outcome would be as follows
id | value
1 | value1
2 | value2
3 | null
4 | value4
I've tried doing it like so;
SELECT [id],[value] FROM [tablea] WHERE [id] IN (1,2,3,4) AND [id] NOT IN (1,2,3,4)
since both arrays will be the same, this returns 0 of course.
I know I can do this with a union, and define the not in statement within the second union, but I'd like to do this without a union.. Any other thoughts?
This is on Microsoft SQL 2005
I unfortunately only have access to SELECT, since I'm performing queries either via VBA or Tableau. So I cannot create a derived table or have anything to reference other than the select statement.
You need a left join of some sort. One way would be to construct your query as:
select v.id, t.value
from (values (1), (2), (3), (4)
) v(id) left join
table t
on v.id = t.id;
Thanks to Joel Coehoorn for the tip towards using a CTE
I was able to accomplish this like so;
WITH numbers AS (
SELECT 1 AS num UNION ALL
SELECT 2 AS num UNION ALL
SELECT 3 AS num UNION ALL
SELECT 4 as num UNION ALL )
SELECT
COALESCE(id,num) as col1,
id as col2
FROM tablea
RIGHT JOIN numbers ON tablea.id = numbers.num
This would return
col1 | col2
1 | 1
2 | 2
3 | NULL
4 | 4