Update column based on IF Else Condition - sql

I have two tables A and B
Table A
ID_number as PK
first_name,
L_Name
Table B
ID_number,
Email_id,
Flag
I have several people who have multiple email ID and are already flagged as X on table B.
Whereas i am trying to find list of people who have an email id or multiple email ID, but were never flagged.
e.g John clark might have 2 email in table B, but was never flagged.

Simply use not exists:
select a.*
from a
where not exists (select 1
from b
where b.id_number = a.id_number and b.flag = 'X'
);

You may want to perform an update, but your question seems to be only about selecting (probably to update based on select). It should be something like this:
SELECT A.L_Name
FROM A
WHERE NOT EXISTS (
SELECT 1
FROM B
WHERE B.ID_number = A.ID_number AND B.Flag = 'X'
)
OR the LEFT JOIN version
SELECT 1
FROM A
LEFT JOIN B ON B.ID_number = A.ID_number AND B.Flag = 'X'
WHER B.ID_number IS NULL
Usually, the first version is faster than the second one.

Forget Table A...
SELECT DISTINCT ID_number FROM table_b t1
WHERE NOT EXISTS(
SELECT NULL FROM table_b t2 WHERE t1.ID_number=t2.ID_number AND t2.flag='X'
)

Judging by your responses in the comments, I believe this is what you are looking for:
--drop table update_test;
create table update_test
(
id_num number,
email_id number,
flag varchar2(1) default null
);
insert into update_test values (1, 1, null);
insert into update_test values (1, 2, null);
insert into update_test values (2, 3, null);
insert into update_test values (2, 7, null);
insert into update_test values (3, 2, null);
insert into update_test values (3, 3, 'X');
insert into update_test values (3, 7, null);
select * from update_test;
select id_num, min(email_id)
from update_test
group by id_num;
update update_test ut1
set flag = case
when email_id = (
select min(email_id)
from update_test ut2
where ut2.id_num = ut1.id_num
) then 'X'
else null end
where id_num not in (
select id_num
from update_test
where Flag is not null);
The last update statement will update and set the Flag field on the record for each id_num group with the lowest email_id. If the id_num group already has the Flag field set for one it will ignore it.

Related

Group by absorb NULL unless it's the only value

I'm trying to group by a primary column and a secondary column. I want to ignore NULL in the secondary column unless it's the only value.
CREATE TABLE #tempx1 ( Id INT, [Foo] VARCHAR(10), OtherKeyId INT );
INSERT INTO #tempx1 ([Id],[Foo],[OtherKeyId]) VALUES
(1, 'A', NULL),
(2, 'B', NULL),
(3, 'B', 1),
(4, 'C', NULL),
(5, 'C', 1),
(6, 'C', 2);
I'm trying to get output like
Foo OtherKeyId
A NULL
B 1
C 1
C 2
This question is similar, but takes the MAX of the column I want, so it ignores other non-NULL values and won't work.
I tried to work out something based on this question, but I don't quite understand what that query does and can't get my output to work
-- Doesn't include Foo='A', creates duplicates for 'B' and 'C'
WITH cte AS (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY [Foo] ORDER BY [OtherKeyId]) rn1
FROM #tempx1
)
SELECT c1.[Foo], c1.[OtherKeyId], c1.rn1
FROM cte c1
INNER JOIN cte c2 ON c2.[OtherKeyId] = c1.[OtherKeyId] AND c2.rn1 = c1.rn1
This is for a modern SQL Server: Microsoft SQL Server 2019
You can use a GROUP BY expression with HAVING clause like below one
SELECT [Foo],[OtherKeyId]
FROM #tempx1 t
GROUP BY [Foo],[OtherKeyId]
HAVING SUM(CASE WHEN [OtherKeyId] IS NULL THEN 0 END) IS NULL
OR ( SELECT COUNT(*) FROM #tempx1 WHERE [Foo] = t.[Foo] ) = 1
Demo
Hmmm . . . I think you want filtering:
select t.*
from #tempx1 t
where t.otherkeyid is not null or
not exists (select 1
from #tempx1 t2
where t2.foo = t.foo and t2.otherkeyid is not null
);
My actual problem is a bit more complicated than presented here, I ended up using the idea from Barbaros Özhan solution to count the number of items. This ends up with two inner queries on the data set with two different GROUP BY. I'm able to get the results I need on my real dataset using a query like the following:
SELECT
a.[Foo],
b.[OtherKeyId]
FROM (
SELECT
[Foo],
COUNT([OtherKeyId]) [C]
FROM #tempx1 t
GROUP BY [Foo]
) a
JOIN (
SELECT
[Foo],
[OtherKeyId]
FROM #tempx1 t
GROUP BY [Foo], [OtherKeyId]
) b ON b.[Foo] = a.[Foo]
WHERE
(b.[OtherKeyId] IS NULL AND a.[C] = 0)
OR (b.[OtherKeyId] IS NOT NULL AND a.[C] > 0)

How to update several elements by id?

I try to update several cells, but nothing, could you help me to optimize my query?
with upd AS (
UPDATE era
SET gender = CASE id
WHEN 3 THEN 'Female'
WHEN 4 THEN 'Male'
END,
SET city = CASE id
WHEN 3 THEN 'Minsk'
WHEN 4 THEN 'Brest'
END
WHERE id IN (3, 4)
returning *
)
select * from upd;
I would suggest that you use a derived table for this logic:
WITH upd AS (
UPDATE era
SET gender = v.gender,
city = v.city
FROM (VALUES (3, 'Female', 'Minsk'), (4, 'Male', 'Brest')
) v(id, gender, city)
WHERE v.id = era.id
RETURNING *
)
SELECT *
FROM upd;
Using this method, you don't have to repeat values or use complicated CASE expressions -- both of which are prone to error.
You just not need second SET
with upd AS (
UPDATE era
SET gender = CASE id
WHEN 3 THEN 'Female'
WHEN 4 THEN 'Male'
END,
city = CASE id
WHEN 3 THEN 'Minsk'
WHEN 4 THEN 'Brest'
END
WHERE id IN (3, 4)
returning *
)
select * from upd;

How to get the aggregate results for missing values as zero

My DDL is like
create table if not exists sample_t
(
id bigserial NOT NULL constraint sample_t_id primary key,
test_value varchar(255),
test varchar(255) not null,
count bigint not null
);
Sample insert queries
INSERT INTO public.sample_t (id, test_value, test, count) VALUES (1, 'CC1', 'hi-1', 11);
INSERT INTO public.sample_t (id, test_value, test, count) VALUES (2, 'CC2', 'hi-1', 10);
INSERT INTO public.sample_t (id, test_value, test, count) VALUES (3, 'CC1', 'hi-2', 4);
My Query is
select test, sum(count) from sample_t where test_value= 'CC2' group by test;
The o/p is
test | sum
hi-1 | 10
However, I want to list down missing 'test' column values as 0. So the expected o/p should look like:
test | sum
hi-1 | 10
hi-2 | 0
Instead, use conditional aggregation:
select test, sum(case when test_value = 'CC2' then count else 0 end)
from sample_t
group by test;
Alternatively, if you have a table of all test values:
select t.test, coalesce(sum(count), 0)
from test t left join
sample_t s
on s.test = t.test and s.test_value = 'CC2'
group by t.test;
The problem here is that your WHERE clause might completely filter off a test group, should none of its records have the matching test value. You may use a left join here to preserve every initial test value:
SELECT DISTINCT
s1.test,
COALESCE(s2.cnt, 0) AS cnt
FROM sample_t s1
LEFT JOIN
(
SELECT test, COUNT(*) AS cnt
FROM sample_t
WHERE test_value = 'CC2'
GROUP BY test
) s2
ON s1.test = s2.test;
Or, you could use conditional aggregation:
SELECT
test, COUNT(CASE WHEN test_value = 'CC2' THEN 1 END) cnt
FROM sample_t
GROUP BY test;

SQL How to find the Data which is not in table

I have this select statement, where I want to show found and not-found in the result set ...
My query gives me only whatever (values) exists in the DB.
How to also add not-found.
Example:
5647994 1234 Data exist in table
5651061 8976 Data exist in table
5823683 null Data not exist in table
6115602 null Data not exist in table
SELECT *
FROM Carrier c
WHERE (SUBSTRING(c.SrcFileName, 14, 7) in (
'5647994',
'5651061',
'5823683',
'6115602',
'6125795',
'6140114',
'6144781',
'6155133')
Try this:
SELECT t1.val,
IF (t2.id IS NULL, 'NOT FOUND', 'FOUND'),
t2.*
FROM (
SELECT '5647994' AS val UNION ALL SELECT '5651061' UNION ALL
SELECT '5823683' UNION ALL SELECT '6115602' UNION ALL
SELECT '6125795' UNION ALL SELECT '6140114' UNION ALL
SELECT '6144781' UNION ALL SELECT '6155133') AS t1
LEFT JOIN Carrier AS t2 ON t1.val = SUBSTRING(t2.SrcFileName, 14, 7)
The idea is to create an in-line table that contains all to-be-searched values. If we LEFT JOIN the original table to this in-line table, then all values are returned.
The above query assumes that id is a field of Carrier table. Checking this field for NULL/NOT NULL values identifies not found / found values respectively.
Updated based upon additional information.
If(OBJECT_ID('tempdb..#TempSrcFileName') Is Not Null) Drop Table #TempSrcFileName
CREATE TABLE #TempSrcFileName
(
src_file_name nchar(7)
)
INSERT INTO #TempSrcFileName (src_file_name)
VALUES
('5647994')
, ('5651061')
, ('5823683')
, ('6115602')
, ('6125795')
, ('6140114')
, ('6144781')
, ('6155133')
;
SELECT
t.src_file_name
, 'Found' AS [Status]
FROM #TempSrcFileName t
LEFT JOIN Carrier c ON SUBSTRING(c.SrcFileName, 14, 7) = t.src_file_name
WHERE (SUBSTRING(c.SrcFileName, 14, 7) IS NOT NULL)
UNION SELECT
t.src_file_name
, 'Not Found' AS [Status]
FROM #TempSrcFileName t
LEFT JOIN Carrier c ON SUBSTRING(c.SrcFileName, 14, 7) = t.src_file_name
WHERE (SUBSTRING(c.SrcFileName, 14, 7) IS NULL)

How to fix a query which mixes AND and OR conditions?

I need to query a table with a filtering condition on three columns:
select *
from table a
where a.order = car
and a.color !=red
or a.automatic !=auto
This just checks the first conditions and ignores the second one.
I just can't get my head around it :(
Its not clear from your question but all you probably need is to wrap parens around your OR condition
Given this following table and rows
CREATE TABLE table_a
(
id int primary key,
a_order varchar2(20),
color varchar2(40),
automatic varchar2(10)
)
/
INSERT INTO table_a VALUES (1,'car','red','auto') /
INSERT INTO table_a VALUES (2,'car','red','manual') /
INSERT INTO table_a VALUES (3,'car','blue','auto') /
INSERT INTO table_a VALUES (4,'car','blue','manual') /
INSERT INTO table_a VALUES (5,'truck','red','auto') /
INSERT INTO table_a VALUES (6,'truck','red','manual') /
INSERT INTO table_a VALUES (7,'truck','blue','auto') /
INSERT INTO table_a VALUES (8,'truck','blue','manual') /
This select statement will return records 2, 3 and 4 since they are all cars and are either blue or manual
SELECT *
FROM table_a a
WHERE a.a_order = 'car'
AND ( a.color != 'red'
OR a.automatic != 'auto' )
Demo
Another option is to do the following using De Morgan's law "The negation of a disjunction is the conjunction of the negations."
SELECT *
FROM table_a a
WHERE a.a_order = 'car'
AND NOT ( a.color = 'red'
AND a.automatic = 'auto' )
Demo
The priority of AND is higher than OR logical operator. You can use OR condition within parenthesis.
SELECT *
FROM table_a a
WHERE a.a_order = 'car'
AND NOT ( a.color = 'red'
AND a.automatic = 'auto' )