Show all numbers that are not in table - sql

I have the following problem.
I have more than 60,000 numbers in my DB2 database and I need to check which of them are not found in my table.
I would like to have the output as follows:
number | found | not-found
010012 |x |
010023 | |x
Depending on that I have to create either an UPDATE or an INSERT.
I already created the UPDATE statement with all the numbers. That`s not the problem, however, finding out which of the numbers are missing or not I cannot figure it out.
This is my SELECT which of course does not work as expected.
SELECT
a.Field1 || a.Field2,
CASE WHEN a.Field1 || a.Field2 IN ('010012', '010023')
THEN 'Found'
ELSE 'None'
END
FROM TEST_TABLE.U56 a
WHERE Field1 || Field2 IN ('010012', '010023');

Try this:
WITH
NUMBERS (NUMBER) AS
(
VALUES
'010012'
, '010023'
)
,
MY_TABLE (NUMBER) AS
(
VALUES
'010012'
, '010012'
)
SELECT DISTINCT
N.NUMBER
, CASE WHEN T.NUMBER IS NOT NULL THEN 'x' END AS FOUND
, CASE WHEN T.NUMBER IS NULL THEN 'x' END AS NOT_FOUND
FROM NUMBERS N
LEFT JOIN MY_TABLE T ON T.NUMBER = N.NUMBER
NUMBER
FOUND
NOT_FOUND
010023
x
010012
x
If it's not what you need, then please, edit your question with a complete example with sample data for both tables and the result desired.

Related

SQL Server 2014 - SQL Case statement on columns

This is the table I have :
For every unique TID, there are 2 records. For a unique TID if both records in a field is populated I want the name of the field. For example, for T01 : Field2 and Field4 have both records populated.
My current approach is I create a column with comma separated values with the field names :
INSERT INTO TEMP
SELECT *,
(CASE WHEN COUNT(IIF(Field1 IS NOT NULL,1,NULL)) = 2 THEN 'FIELD1' ELSE 'NO' END) + ',' +
(CASE WHEN COUNT(IIF(Field2 IS NOT NULL,1,NULL)) = 2 THEN 'FIELD2' ELSE 'NO' END) + ',' +
(CASE WHEN COUNT(IIF(Field3 IS NOT NULL,1,NULL)) = 2 THEN 'FIELD3' ELSE 'NO' END) + ',' +
(CASE WHEN COUNT(IIF(Field4 IS NOT NULL,1,NULL)) = 2 THEN 'FIELD4' ELSE 'NO' END) AS ATTR
FROM ORIGINAL_TABLE;
I then convert the comma separated column into multiple records :
SELECT *, S.ITEMS as ATTRIBUTES
FROM TEMP
CROSS APPLY DBO.SPLIT(ATTR, ',') S
WHERE S.ITEMS NOT LIKE '%NO%'
Consider T101 of the result obtained from above command, This gives me the output :
Edit : Apologies. It should be Field2 instead of Field1.
This does give me information on the fields for every unique TID that follows the condition but I want it to be more specific. I run this for very big data with over 100 columns so this approach is slow.
Is there a way to get this? Where I display just the fields that satisfy the condition and their values for both records in T101.
Edit : Apologies. It should be Field2 instead of Field1 in the table.
I am fairly new to SQL, any help would be much appreciated!
Your question is rather complicated, and I'm not 100% sure what you really want. But based on:
For a unique TID if both records in a field is populated I want the name of the field.
You can unpivot and aggregate. Assuming that your columns all have a similar data type, you can use:
SELECT t.tId, v.fieldname
FROM ORIGINAL_TABLE t CROSS APPLY
(VALUES ('Field1', Field1),
('Field2', Field2),
('Field3', Field3),
('Field4', Field4)
) v(fieldname, val)
GROUP BY t.tID, v.fieldname
HAVING COUNT(*) = COUNT(v.val) -- all populated

SQL Replace Not working for Greater Than >

I'm trying to count all values in a column Value that are over 5.
However, some results in that column appear like '>10' (it has the greater than symbol > in the field)
I'd like to still count that as > 5.
To do that, I did the following:
(COUNT(CASE WHEN t.VALUE LIKE '*>*'
and Replace(t.VALUE, '>', ' ') > 5)
Then 1
Else NULL
End
)
But, for whatever reason, it's not replacing.
Well, how about converting to a number?
select sum(case when try_convert(int, replace(t.value, '>', '')) > 5
then 1 else 0
end) as values_over_5
Your data model is suspicious, because you are doing numeric comparisons on a column that contains numbers.
A couple of things.
The asterisk isn't a valid wildcard character in SQL Server, so we'll change that.
Also, if you want the string to become a number, you'll want to replace the greater-than with an empty string, not a space. It doesn't affect the outcome, but it's the right thing to do.
This isn't as elegant as Gordon's one-liner, but it produced the expected results.
DECLARE #t TABLE (VALUE VARCHAR(5));
INSERT #t (VALUE)
VALUES ('1'),('10'),('>10');
SELECT COUNT(*) AS Over5
FROM
(
SELECT
CASE WHEN t.VALUE LIKE '%>%' THEN Replace(t.VALUE, '>', '')
ELSE t.VALUE
END AS NewVal
FROM #t as t
) AS d
WHERE NewVal > 5;
+-------+
| Over5 |
+-------+
| 2 |
+-------+

Search SQL and return true or false

I have a table that has thousands of rows in. I need to check if certain values exists in the table or not.
I want to list all the bar codes I am searching with a flag of true or false returned if there is one.
I have come up with this so far:
SELECT CASE WHEN EXISTS (
SELECT *
FROM TABLE
WHERE Coulmn in ('a','b', 'c', 'd', 'e', 'f', 'g')
)
THEN CAST(1 AS BIT)
ELSE CAST(0 AS BIT) END
This however just returns a value of 1.
So in the table I have
coulmn
----------
A
B
D
E
F
G
I want to do a search that returns the following
Coulmn | Exsists
-----------------
A | True
B | True
C | False
D | True
E | True
F | True
G | True
You can use a query like the following:
SELECT t1.v,
CASE WHEN t2.col IS NOT NULL THEN 'true' ELSE 'false' END AS Exists
FROM (
SELECT 'a' AS v UNION ALL SELECT 'b' UNION ALL SELECT 'c' UNION ALL SELECT 'd'
UNION ALL SELECT 'e' UNION ALL SELECT 'f' UNION ALL SELECT 'g') AS t1
LEFT JOIN mytable AS t2 ON t1.v = t2.col
This works:
SELECT *, CASE WHEN (Column in ('1','2')) THEN CAST(1 AS BIT) ELSE CAST(0 AS BIT) END AS result_field
FROM TABLE;
NOTE: Tested in PostgreSQL
As it's written, the outer select is select case when exists () then 1 else 0 end... so it is only going to return one row. The outer select must include "Column" AND "Exists" (select column, ...) to return two columns.
A "where" clause will never return a "false" like this, though, because "column" has to be in a real table for the query to actually return it. As #jarlh says, you'll need a helper table to store the columns you're looking for:
Create table SearchColumns (SearchColumn char(1));
insert into SearchColumns (SearchColumn)
values ('A'), ('B'), ('C'), ('D'), ('E'), ('F'), ('G'), ('H')
Then you can do the If Exists to your table from that table to see which values are in or not in:
select SearchColumn, case when exists
(select * from TABLE where Table.Column = SearchColumns.SearchColumn)
then 'True' else 'False' end as ExistsStatus
from SearchColumns
I think that will get you what you want. This gets a) Only one record per column no matter how many times it occurs in your table and b) "True" and "False" for every column value you're looking for. If you really wanted a Bit, you can use 0 and 1 and the casting from the original query, but they actually show "0" and "1"; and c) this should work no matter how many values you have.
(Note, I assumed some of those were spelling errors, so I made adjustments, but they were consistent so I'm not certain).
With the help form above I created a temp table and then implemented one of the soultions shared.
CREATE TABLE #Temp
(
Barcode VARCHAR (100)
)
INSERT INTO #Temp
VALUES
(1),
(2),
(3),
(4 )
select barcode, case when exists
(select * from CIPKORHHTProductDetails where CIPKORHHTProductDetails.Barcode = #temp.barcode)
then 'True' else 'False' end as ExistsStatus
from #temp order by ExistsStatus DESC

Rank columns by their count of values

I have a table with a bunch of boolean columns. I'd like to rank these columns by the count of true values each one has.
I found a way to count the number of true values in a column using:
SELECT count(CASE WHEN col1 THEN 1 ELSE null END) as col1,
count(CASE WHEN col2 THEN 1 ELSE null END) as col2
....
FROM my_table;
but this approach has two problems:
I have to manually type the names of the columns
I have to then transpose the result and order by value
Is there a way to do the whole operation one query?
This is not actually a crosstab job (or "pivot" in other RDBMS), but the reverse operation, "unpivot" if you will. One elegant technique is a VALUES expression in a LATERAL join.
The basic query can look like this, which takes care of:
I have to then transpose the result and order by value
SELECT c.col, c.ct
FROM (
SELECT count(col1 OR NULL) AS col1
, count(col2 OR NULL) AS col2
-- etc.
FROM tbl
) t
, LATERAL (
VALUES
('col1', col1)
, ('col2', col2)
-- etc.
) c(col, ct)
ORDER BY 2;
That was the simple part. Your other request is harder:
I have to manually type the names of the columns
This function takes your table name and retrieves meta data from the system catalog pg_attribute. It's a dynamic implementation of the above query, safe against SQL injection:
CREATE OR REPLACE FUNCTION f_true_ct(_tbl regclass)
RETURNS TABLE (col text, ct bigint)
LANGUAGE plpgsql AS
$func$
BEGIN
RETURN QUERY EXECUTE (
SELECT format('
SELECT c.col, c.ct
FROM (SELECT %s FROM tbl) t
, LATERAL (VALUES %s) c(col, ct)
ORDER BY 2 DESC'
, string_agg (format('count(%1$I OR NULL) AS %1$I', attname), ', ')
, string_agg (format('(%1$L, %1$I)', attname), ', ')
)
FROM pg_attribute
WHERE attrelid = _tbl -- valid, visible, legal table name
AND attnum >= 1 -- exclude tableoid & friends
AND NOT attisdropped -- exclude dropped columns
AND atttypid = 'bool'::regtype -- only character types
);
END
$func$;
Call:
SELECT * FROM f_true_ct('tbl'); -- table name optionally schema-qualified
Result:
col | ct
------+---
col1 | 3
col3 | 2
col2 | 1
Works for any table to rank all boolean columns by their count of true values.
To understand the function parameter, read this:
Table name as a PostgreSQL function parameter
Related answers with more explanation:
Check whether empty strings are present in character-type columns
Replace empty strings with null values
If I understand correctly, you can do this with a giant union all:
select c.*
from ((select 'col1' as which, sum(case when col1 then 1 else 0 end) as cnt from t
) union all
(select 'col2' as which, sum(case when col2 then 1 else 0 end) as cnt from t
) union all
. . .
) c
order by cnt desc;
Although you still need to type the results, this does sidestep the transpositions.

SQL using CASE in SELECT with GROUP BY. Need CASE-value but get row-value

so basicially there is 1 question and 1 problem:
1. question - when I have like 100 columns in a table(and no key or uindex is set) and I want to join or subselect that table with itself, do I really have to write out every column name?
2. problem - the example below shows the 1. question and my actual SQL-statement problem
Example:
A.FIELD1,
(SELECT CASE WHEN B.FIELD2 = 1 THEN B.FIELD3 ELSE null FROM TABLE B WHERE A.* = B.*) AS CASEFIELD1
(SELECT CASE WHEN B.FIELD2 = 2 THEN B.FIELD4 ELSE null FROM TABLE B WHERE A.* = B.*) AS CASEFIELD2
FROM TABLE A
GROUP BY A.FIELD1
The story is: if I don't put the CASE into its own select statement then I have to put the actual rowname into the GROUP BY and the GROUP BY doesn't group the NULL-value from the CASE but the actual value from the row. And because of that I would have to either join or subselect with all columns, since there is no key and no uindex, or somehow find another solution.
DBServer is DB2.
So now to describing it just with words and no SQL:
I have "order items" which can be divided into "ZD" and "EK" (1 = ZD, 2 = EK) and can be grouped by "distributor". Even though "order items" can have one of two different "departements"(ZD, EK), the fields/rows for "ZD" and "EK" are always both filled. I need the grouping to consider the "departement" and only if the designated "departement" (ZD or EK) is changing, then I want a new group to be created.
SELECT
(CASE WHEN TABLE.DEPARTEMENT = 1 THEN TABLE.ZD ELSE null END) AS ZD,
(CASE WHEN TABLE.DEPARTEMENT = 2 THEN TABLE.EK ELSE null END) AS EK,
TABLE.DISTRIBUTOR,
sum(TABLE.SOMETHING) AS SOMETHING,
FROM TABLE
GROUP BY
ZD
EK
TABLE.DISTRIBUTOR
TABLE.DEPARTEMENT
This here worked in the SELECT and ZD, EK in the GROUP BY. Only problem was, even if EK was not the designated DEPARTEMENT, it still opened a new group if it changed, because he was using the real EK value and not the NULL from the CASE, as I was already explaining up top.
And here ladies and gentleman is the solution to the problem:
SELECT
(CASE WHEN TABLE.DEPARTEMENT = 1 THEN TABLE.ZD ELSE null END) AS ZD,
(CASE WHEN TABLE.DEPARTEMENT = 2 THEN TABLE.EK ELSE null END) AS EK,
TABLE.DISTRIBUTOR,
sum(TABLE.SOMETHING) AS SOMETHING,
FROM TABLE
GROUP BY
(CASE WHEN TABLE.DEPARTEMENT = 1 THEN TABLE.ZD ELSE null END),
(CASE WHEN TABLE.DEPARTEMENT = 2 THEN TABLE.EK ELSE null END),
TABLE.DISTRIBUTOR,
TABLE.DEPARTEMENT
#t-clausen.dk: Thank you!
#others: ...
Actually there is a wildcard equality test.
I am not sure why you would group by field1, that would seem impossible in your example. I tried to fit it into your question:
SELECT FIELD1,
CASE WHEN FIELD2 = 1 THEN FIELD3 END AS CASEFIELD1,
CASE WHEN FIELD2 = 2 THEN FIELD4 END AS CASEFIELD2
FROM
(
SELECT * FROM A
INTERSECT
SELECT * FROM B
) C
UNION -- results in a distinct
SELECT
A.FIELD1,
null,
null
FROM
(
SELECT * FROM A
EXCEPT
SELECT * FROM B
) C
This will fail for datatypes that are not comparable
No, there's no wildcard equality test. You'd have to list every field you want tested individually. If you don't want to test each individual field, you could use a hack such as concatenating all the fields, e.g.
WHERE (a.foo + a.bar + a.baz) = (b.foo + b.bar + b.az)
but either way, you're listing all of the fields.
I might tend to solve it something like this
WITH q as
(SELECT
Department
, (CASE WHEN DEPARTEMENT = 1 THEN ZD
WHEN DEPARTEMENT = 2 THEN EK
ELSE null
END) AS GRP
, DISTRIBUTOR
, SOMETHING
FROM mytable
)
SELECT
Department
, Grp
, Distributor
, sum(SOMETHING) AS SumTHING
FROM q
GROUP BY
DEPARTEMENT
, GRP
, DISTRIBUTOR
If you need to find all rows in TableA that match in TableB, how about INTERSECT or INTERSECT DISTINCT?
select * from A
INTERSECT DISTINCT
select * from B
However, if you only want rows from A where the entire row matches the values in a row from B, then why does your sample code take some values from A and others from B? If the row matches on all columns, then that would seem pointless. (Perhaps your question could be explained a bit more fully?)