show bool if row exists or doesn't - tsql

I have the table [Contracts] with columns [id], [number]. And I also have some numbers in the string format: '12342', '23252', '1256532'. I want to get the output something like this.
1535325 | no
12342 | yes
23252 | yes
434574 | no
1256532 | yes
of course I can write this and get the rows i have, but how can I determine if the row doesn't exist and get the output above:
SELECT [Id]
,[Number]
FROM [Contracts]
where [Number] in
('12342', '23252', '1256532')

You can put values into temporary table or a table variable and do left join:
declare #d table (Number varchar(10))
insert into #d values ('12342'), ('23252'), ('1256532'), ('xxxx') -- last one is not in Contracts
SELECT c.[Id], c.[Number], case when d.Number is NULL then 'no' else 'yes' end [This Number from C is in D also]
FROM [Contracts] c
left join #d d on d.Number = c.Number
for "opposite" use right join
SELECT c.[Id], d.[Number], case when c.Number is NULL then 'no' else 'yes' end [This Number from D is in C also]
FROM [Contracts] c
right join #d d on d.Number = c.Number

Related

In PostgreSQL, how can I optimize a query with which I obtain the differences between the current column and the immediately previous one?

I have this audit table
User
date
text
text 2
u1
2023-01-01
hi
yes
u1
2022-12-20
hi
no
u1
2022-12-01
hello
maybe
And I need as a result, something like this:
User
date
text
text 2
u1
2023-01-01
null
x
u1
2022-12-20
x
x
u1
2022-12-01
null
null
So I can know which column changed from the last time.
Something like this is working, but I think may be a way to optimize it? or at least generate a "more easy to look" query? (i need the information for almost 20 columns, not only 3)
SELECT
ta.audit_date,
ta.audit_user,
CASE
WHEN ta.audit_operation = 'I' THEN 'Insert'
WHEN ta.audit_operation = 'U' THEN 'Update'
END AS action,
CASE WHEN ta.column1 <> (SELECT column1
FROM audit_table ta1
WHERE ta1.id = 9207 AND ta1.audit_date < ta.audit_date
ORDER BY ta1.audit_date DESC
LIMIT 1)
THEN 'X' ELSE null END column1,
CASE WHEN ta.column2 <> (SELECT column2
FROM audit_table ta1
WHERE ta1.id = 9207 AND ta1.audit_date < ta.audit_date
ORDER BY ta1.audit_date DESC
LIMIT 1)
THEN 'X' ELSE null END column2,
CASE WHEN ta.column3 <> (SELECT column3
FROM audit_table ta1
WHERE ta1.id = 9207 AND ta1.audit_date < ta.audit_date
ORDER BY ta1.audit_date DESC
LIMIT 1)
THEN 'X' ELSE null END column3
FROM
audit_table ta
WHERE
ta.id = 9207
ORDER BY
audit_date DESC
Thank you!
I think you can just use the LAG() analytic function here. If I understand correctly:
SELECT *, CASE WHEN text != LAG(text) OVER (ORDER BY date) THEN 'x' END AS text_label,
CASE WHEN text2 != LAG(text) OVER (ORDER BY date) THEN 'x' END AS text2_label
FROM yourTable
ORDER BY date;

Handle Null in jsonb_array_elements

I have 2 tables a and b
Table a
id | name | code
VARCHAR VARCHAR jsonb
1 xyz [14, 15, 16 ]
2 abc [null]
3 def [null]
Table b
id | name | code
1 xyz [16, 15, 14 ]
2 abc [null]
I want to figure out where the code does not match for same id and name. I sort code column in b b/c i know it same but sorted differently
SELECT a.id,
a.name,
a.code,
c.id,
c.name,
c.code
FROM a
FULL OUTER JOIN ( SELECT id,
name,
jsonb_agg(code ORDER BY code) AS code
FROM (
SELECT id,
name,
jsonb_array_elements(code) AS code
FROM b
GROUP BY id,
name,
jsonb_array_elements(code)
) t
GROUP BY id,
name
) c
ON a.id = c.id
AND a.name = c.name
AND COALESCE (a.code, '[]'::jsonb) = COALESCE (c.code, '[]'::jsonb)
WHERE (a.id IS NULL OR c.id IS NULL)
My answer in this case should only return id = 3 b/c its not in b table but my query is returning id = 2 as well b/c i am not handling the null case well enough in the inner subquery
How can i handle the null use case in the inner subquery?
demo:db<>fiddle
The <# operator checks if all elements of the left array occur in the right one. The #> does other way round. So using both you can ensure that both arrays contain the same elements:
a.code #> b.code AND a.code <# b.code
Nevertheless it will be accept as well if one array contains duplicates. So [42,42] will be the same as [42]. If you want to avoid this as well you should check the array length as well
AND jsonb_array_length(a.code) = jsonb_array_length(b.code)
Furthermore you might check if both values are NULL. This case has to be checked separately:
a.code IS NULL and b.code IS NULL
A little bit shorter form is using the COALESCE function:
COALESCE(a.code, b.code) IS NULL
So the whole query could look like this:
SELECT
*
FROM a
FULL OUTER JOIN b
ON a.id = b.id AND a.name = b.name
AND (
COALESCE(a.code, b.code) IS NULL -- both null
OR (a.code #> b.code AND a.code <# b.code
AND jsonb_array_length(a.code) = jsonb_array_length(b.code) -- avoid accepting duplicates
)
)
After that you are able to filter the NULL values in the WHERE clause

T-SQL how to join with one column a string and one an integer

How to join with one column a string and one an integer?
--PEOPLE_ID 000092437, PersonID 92437
select PC.PEOPLE_ID, Idn.PersonId,'Home Row 1', PC.Phone1 from #NextIdentityID Idn INNER JOIN PEOPLECHANGES PC on Idn.People_ID = PC.People_ID --PEOPLE_ID 000092437, PersonID 92437 one is varchar, one is integer
union all select PC.PEOPLE_ID, Idn.PersonId,'Office Row 2', PC.Phone2 from #NextIdentityID Idn INNER JOIN PEOPLECHANGES PC on Idn.People_ID = PC.People_ID
union all select PC.PEOPLE_ID, Idn.PersonId,'Cell Row 3', PC.Phone3 from #NextIdentityID Idn INNER JOIN PEOPLECHANGES PC on Idn.People_ID = PC.People_ID
To make sure your varchar() data doesn't raise any errors you should check to see if it can be converted into an integer. One way to do this is with a case statement in the where clause. If it is not convertible then your join won't work - but at least your query can still run with out error.
This example shows how you can avoid potential errors.
create table #tempa(id int, descr varchar(50));
create table #tempb(id varchar(10), descr varchar(50));
insert into #tempa(id,descr) values (1234,'Body getta body getta');
insert into #tempb(id,descr) values ('001234','sis boom ba - rah rah rah');
insert into #tempa(id,descr) values (5678,'Weagle Weagle War Damn Eagle');
insert into #tempb(id,descr) values ('0005678','Kickem in the butt Big blue');
insert into #tempa(id,descr) values (9012,'this wont have a match');
insert into #tempb(id,descr) values ('x0912','sis boom ba');
Select a.id as a_id, b.id as b_id
,a.descr as a_descr, b.descr as b_descr
from #tempa a
left join #tempb b
on a.id = case when isnumeric(b.id) = 1 then cast(b.id as int) else 0 end
-- this one will raise an error
Select a.id as a_id, b.id as b_id
,a.descr as a_descr, b.descr as b_descr
from #tempa a
left join #tempb b
on a.id = b.id
drop table #tempa;
drop table #tempb;
If you convert the one with leading zeros to an integer you will get equal values:
SELECT CONVERT(INT, '000092437') = 92437
However, this assumes that all of your varchar column can be convert to int.
If that's not the case then you have to write a function to go the other way and add leading zeros.

Postgresql table name or alias in SELECT and WHERE clauses without specifying column name

I have two tables:
CREATE TABLE a (id INT NOT NULL);
CREATE TABLE b (id INT NOT NULL);
INSERT INTO a VALUES (1), (2);
INSERT INTO b VALUES (1);
If I try to get records from a for which there are records in b (query 1):
SELECT a.id, b FROM a LEFT JOIN b on a.id = b.id WHERE b is NOT NULL;
I get:
id | b
----+-----
1 | (1)
If I try to get records from a for which there are NO records in b (query 2):
SELECT a.id, b FROM a LEFT JOIN b on a.id = b.id WHERE b IS NULL;
I get:
id | b
----+---
2 |
It seems OK.
Then I alter b:
ALTER TABLE b ADD COLUMN s TEXT NULL;
then query 1 does not return any rows, query 2 returns the same rows and
SELECT a.id, b FROM a LEFT JOIN b on a.id = b.id;
returns
id | b
----+------
1 | (1,)
2 |
My questions are:
Why does Postresql allow to use table name or alias in WHERE clause without specifying column name?
What is (1,) in column b of resulting rows?
Why does (1,) not satisfy IS NULL and IS NOT NULL in query 1 and query 2?
P.S. If I alter table b as ALTER TABLE b ADD COLUMN s TEXT NOT NULL DEFAULT '' instead then queries 1 and 2 return the same rows.
Answering by questions:
This is row constructor, so every value from a column builds up a row value (composite value) using values from your columns for its member fields
(1,) is a row constructor with first member being 1 and second member (your text field) which has a null value, thus no value is shown.
You're comparing entire row constructor which actually satisfies both of comparison (is null and is not null)
More on point 3:
select *, b is not null as b_not_null, b is null as b_null from b;
Reult:
id | b_not_null | b_null
----+------------+--------
1 | t | f
A row IS NULL when all of its members have NULL values, otherwise it IS NOT NULL. Reproduce:
create table rowtest ( col1 int, col2 int);
insert into rowtest values (null,null), (1,1), (null,1);
select
col1, col2, rowtest,
case when rowtest is null then true else false end as rowtest_null
from rowtest;
Result:
col1 | col2 | rowtest | rowtest_null
------+------+---------+--------------
| | (,) | t
1 | 1 | (1,1) | f
| 1 | (,1) | f
Actually, for your queries they both could be rewritten to:
Query1: Get records from a with matching records from b
Using INNER JOIN which actually is the same as JOIN:
SELECT a.id, b FROM a JOIN b on a.id = b.id;
Query2: Get records from a with no matching records from b
Using NOT EXISTS instead of LEFT JOIN:
SELECT a.id
FROM a
WHERE NOT EXISTS (
SELECT 1
FROM b
WHERE a.id = b.id
);
For the last query if you really need the second empty column you can add a static value to select list like that:
SELECT a.id, null as b
The table name can be used in the SELECT or WHERE to refer to a record value containing the entire row of the table. In the output of psql a record will appear like (1) (if it has one field), or (1,2) (if it has two fields), etc. The (1,) that you see is a record with two fields that contain the values 1 and NULL. A value of record type can be null, e.g. in a left join if there is no matching row for the second table.

Selecting an actual MIN value instead of NULL in SQL query

From this table:
Select * into #tmp from (
select 'a' A, 'b' B, NULL C union all
select 'aa' A, 'ab' B, 1 C union all
select 'aaa' A, 'bbb' B, 2 C ) x
I'd like to get this result:
A B Val
a b 1
aa ab 1
aaa bbb 2
That is, take the non-null min value and replace the NULL.
I suppose I could join the table to a filtered version of itself where no nulls appear. But that seems overkill. I thought this might be able to be done in the MIN Aggregate clause itself.
Any ideas would be helpful, thanks!
declare #null int
select #null = MIN(c) from #tmp
select A,
B,
ISNULL(c,#null) as val1
from #tmp
or
select A,
B,
ISNULL(c,(select MIN(c) from #tmp)) as val1
from #tmp
EDIT: I wrote "You want something like ISNULL(c, MIN(c)) but that's not possible."
But I'm wrong, it is possible. I was missing something in my syntax, so #kiki47's answer is exactly what you are asking for.
I wouldn't phrase it as "I suppose I could join the table to a filtered version of itself where no nulls appear," but more or less you can get the min and the use it.
In one go:
WITH cte AS (
SELECT MIN(c) minVal FROM #tmp WHERE c IS NOT NULL
)
SELECT a, b, ISNULL(c, cte.minVal)
FROM #tmp
CROSS JOIN cte
or maybe simpler (but may optimize to the same thing):
DECLARE #minVal INTEGER
SELECT #minVal = MIN(c) FROM #tmp WHERE c IS NOT NULL
SELECT a, b, ISNULL(c, #minVal) FROM #tmp