insert into with two selects and compare - sql

It looks stupid in this example but here is what I want to do:
Table_a:
id fk_b_id full_name
1 [I want '10' here] [I want 'John, Doe' here]
Table_b:
id first_name
10 John
Table_c:
id full_name date
20 John, Doe 2020-01-01
I get all the full names that is not already in c and meets the criteria like this:
select distinct full_name
from Table_c
where full_name not in (
select full_name
from Table_a ) and date > GETDATE()
The result from the query should be inserted into Table_a together with Table_b's id (fk_b_id). So I need a compare between part of Table_a's full_name and Table_b's first_name to get the correct fk. I can do the compare like this:
where Table_b.first_name = LTRIM(RTRIM(RIGHT(Table_c.full_name, CHARINDEX(',', REVERSE(Table_c.full_name))-1 )))
Can I insert all the full names from table c (that meets the criteria) and the fk from table b (where their first names match) into table a in one query?

Sure, just use your condition to JOIN between tables B and C
INSERT INTO Table_a (fk_b_id, full_name)
SELECT DISTINCT b.id, c.full_name
FROM Table_c c
INNER JOIN Table_b b ON b.first_name = LTRIM(RTRIM(RIGHT(c.full_name, CHARINDEX(',', REVERSE(c.full_name))-1 )))
WHERE c.full_name not in (
select a.full_name
from Table_a a ) and c.date > GETDATE()
EDIT:
Also note, that if your table_a.full_name column can have NULL values, NOT IN sub-query will fail to give you expected results. I suggest rewriting it to use NOT EXISTS
WHERE NOT EXISTS
(SELECT * FROM Table_a WHERE Table_a.Full_Name = Table_c.FUll_Name )

Related

Returning the first result from another table if statement is true

So I'm struggling to find the logic for the next problem:
I got 2 tables
TABLE A has the following column
Postalcode
1111
2222
3333
4444
TABLE B has the following column
Postalcode
1111AA
1111BB
1111CA
2222AA etc
What I would like to have is that if the Postalcodes first 4 numbers are found from Table A in table B, then I would like to have the first result of that postalcode from Table B (4digits+2letters).
e.g. if the postalcode in A is 1111 and substring(postalcode, 1, 4) of Table B is also 1111, then return the first result of that postalcode from Table B --> 1111AA
I can't seem to find the answer for this and I'm struggling for a while now.
Hope you guys have the solution for me.
If for each record in table A you want to match at most one record from Table B, an OUTER APPLY (SELECT TOP 1 ...) should do the trick.
Try:
select a.PostalCode, b1.Postalcode
from table_a a
outer apply (
select top 1 *
from table_b b
where b.Postalcode LIKE a.Postalcode + '%'
order by b.id
) b1
order by a.PostalCode;
If you wish to omit results that have no matching table_b record, change the OUTER APPLY to a CROSS APPLY. A OUTER APPLY is like a LEFT JOIN while a CROSS APPLY is like an INNER JOIN.
See this db<>fiddle fr a demo.
(Credit Bernd Buffen for the data setup. Note that I changed PostalCode from INT to VARCHAR to simplify the match criteria.)
i have change the sample from #Ergest Basha with a virtual column and index
CREATE TABLE table_a (
Postalcode INT ,
KEY idx_sPortalcode (Postalcode)
);
INSERT INTO table_a VALUES
(1111),
(2222),
(3333),
(4444);
CREATE TABLE table_b (
id INT,
Postalcode VARCHAR(25),
sPostalcode INT AS ( 0 + Postalcode) STORED,
KEY idx_sPortalcode (sPostalcode)
);
INSERT INTO table_b (id,Postalcode) VALUES
(1,'1111AA'),
(2,'1111BB'),
(3,'1111CA'),
(4,'2222AA');
SELECT * FROM table_b;
-- EXPLAIN
SELECT b.Postalcode
FROM table_a a
INNER JOIN table_b b ON b.sPostalcode=a.Postalcode
WHERE a.Postalcode=1111
ORDER BY b.id ASC LIMIT 1;
Something like this: MySQL
select b.Postalcode
from table_a a
inner join table_b b on LEFT(b.Postalcode,4)=a.Postalcode
where a.Postalcode=1111
order by b.id asc limit 1;
Check the demo
SQL Server
select top(1) b.Postalcode
from table_a a
inner join table_b b on LEFT(b.Postalcode,4)=a.Postalcode
where a.Postalcode=1111
order by b.id ;
Demo
Edit based on comments*
I think you need something like below, but check #Bernd Buffen suggestion for performance:
WITH cte AS (
SELECT Postalcode, ROW_NUMBER() OVER ( PARTITION BY LEFT(Postalcode,4) ORDER BY id asc ) row_num
FROM table_b
)
SELECT cte.Postalcode
FROM table_a a
INNER JOIN cte on LEFT(cte.Postalcode,4)=a.Postalcode
WHERE row_num = 1 ;
Demo

How to update a repeated non-record field on BigQuery?

I have seen questions on how to update a column that is a repeated record type of data, all using structs, but I cannot find how to update a column that just a repeated string type and was generated using array_agg function.
The update I'm trying to perform is based on another table. On table A I have 2 columns: id and phone_number. phone_number is an array_agg type of column. On table B I have lots of ids and more phone numbers, but the table does not have any array. Its just a normal table with nothing fancy on it.
Both column types on table B are nullable string. On table A only id is a nullable string and phone_number is a repeated string.
When table A was created (using a query) the phone_number column was limited to 5 numbers only (array_agg(phone limit 5) as phone_number).
How can I update numbers in table A based on ids and numbers on table B? I want to update the phone numbers of table A which ids are present on table B.
What I have tried so far is this:
update `table_A`
set phone_number = array(
select phone_number from (
select phone_number ,row_number() over(partition by id) rnum
from `table_B`
)
where rnum <= 5
)
where id in (select distinct id from `table_B`)
The phone numbers you want to add to each id are:
select b.id, array_agg(b.phone_number)
from `table_B` b
where b.phone_number not in (select a.pn
from `table_A` a cross join
unnest(a.phone_numbers) pn
where a.id = b.id
)
group by b.id
You can incorporate this into an update and append the arrays to each other:
update `table_A` a
set a.phone_numbers = array_concat(a.phone_numbers, b.phone_numbers)
from (select b.id, array_agg(b.phone_number) as phone_numbers
from `table_B` b
where b.phone_number not in (select a.pn
from `table_A` a cross join
unnest(a.phone_numbers) pn
where a.id = b.id
)
group by b.id
) b
where b.id = a.id

Select rows using EXCEPT - SQL Server

How can I return just different columns when I use except in SQL Server?
Example:
SELECT ID, NAME FROM TABLE_B
EXCEPT
SELECT ID, NAME FROM TABLE_A
In this case, if there is different name return just show name column.
Your code is correct. You won't get any repeated row (that's ID + NAME!).
But if I understand correctly, you only want to focus in names. Then remove ID from selected fields:
SELECT NAME FROM TABLE_B
EXCEPT
SELECT NAME FROM TABLE_A
[Edited, regarding a comment:]
This shows distinct rows from TABLE_B that aren’t in TABLE_A. This is the goal of using EXCEPT. For anything else, EXCEPT is not the solution.
In case you're looking for all diferent names from both tables, you can use:
select distinct NAME
from
(select NAME from TABLE_A
UNION
select NAME from TABLE_B) as T
You can get a result set which flags non-existing data in the second table in the form
ID flag_ID NAME flag_Name
1 ! A ! -- No Id=1, no NAME ='A' exists in the second table
3 NULL NULL ! -- Id=3 exists, no NAME is NULL exists
4 NULL Y NULL -- Both values exist but never in the same row
and proceed with a criteria you need.
Assuming ID is NOT NULL, NAME is nullable, NULLs should be considered "equal":
SELECT b.ID,
CASE WHEN NOT EXISTS (SELECT 1 FROM a t2 WHERE t2.ID=b.ID) THEN '!' END flag_ID,
b.NAME,
CASE WHEN NOT EXISTS (SELECT 1 FROM a t2
WHERE ISNULL(NULLIF(b.NAME, t2.NAME), NULLIF(t2.NAME, b.NAME)) IS NULL)
THEN '!' END flag_Name
FROM b
LEFT JOIN a ON a.ID = b.ID
AND ISNULL(NULLIF(a.NAME, b.NAME), NULLIF(b.NAME, a.NAME)) IS NULL
WHERE a.ID IS NULL
OR ISNULL(NULLIF(a.NAME, b.NAME), NULLIF(b.NAME, a.NAME)) IS NOT NULL

Value present in more than one table

I have 3 tables. All of them have a column - id. I want to find if there is any value that is common across the tables. Assuming that the tables are named a.b and c, if id value 3 is present is a and b, there is a problem. The query can/should exit at the first such occurrence. There is no need to probe further. What I have now is something like
( select id from a intersect select id from b )
union
( select id from b intersect select id from c )
union
( select id from a intersect select id from c )
Obviously, this is not very efficient. Database is PostgreSQL, version 9.0
id is not unique in the individual tables. It is OK to have duplicates in the same table. But if a value is present in just 2 of the 3 tables, that also needs to be flagged and there is no need to check for existence in he third table, or check if there are more such values. One value, present in more than one table, and I can stop.
Although id is not unique within any given table, it should be unique across the tables; a union of distinct id should be unique, so:
select id from (
select distinct id from a
union all
select distinct id from b
union all
select distinct id from c) x
group by id
having count(*) > 1
Note the use of union all, which preserves duplicates (plain union removes duplicates).
I would suggest a simple join:
select a.id
from a join
b
on a.id = b.id join
c
on a.id = c.id
limit 1;
If you have a query that uses union or group by (or order by, but that is not relevant here), then you need to process all the data before returning a single row. A join can start returning rows as soon as the first values are found.
An alternative, but similar method is:
select a.id
from a
where exists (select 1 from b where a.id = b.id) and
exists (select 1 from c where a.id = c.id);
If a is the smallest table and id is indexes in b and c, then this could be quite fast.
Try this
select id from
(
select distinct id, 1 as t from a
union all
select distinct id, 2 as t from b
union all
select distinct id, 3 as t from c
) as t
group by id having count(t)=3
It is OK to have duplicates in the same table.
The query can/should exit at the first such occurrence.
SELECT 'OMG!' AS danger_bill_robinson
WHERE EXISTS (SELECT 1
FROM a,b,c -- maybe there is a place for old-style joins ...
WHERE a.id = b.id
OR a.id = c.id
OR c.id = b.id
);
Update: it appears the optimiser does not like carthesian joins with 3 OR conditions. The below query is a bit faster:
SELECT 'WTF!' AS danger_bill_robinson
WHERE exists (select 1 from a JOIN b USING (id))
OR exists (select 1 from a JOIN c USING (id))
OR exists (select 1 from c JOIN b USING (id))
;

How to get same results without using distinct in query

I have a table with data like so:
[ID, Name]
1, Bob
1, Joe
1, Joe
1, Bob
I want to retrieve a list of records showing the relationship between the records with the same ID.
For instance, I want the following result set from my query:
Bob, Joe
Joe, Bob
Bob, Bob
Joe, Joe
This shows me the "from" and "to" for every item in the table.
I can get this result by using the following query:
SELECT DISTINCT [NAME]
FROM TABLE A
INNER JOIN TABLE B ON A.ID = B.ID
Is there anyway for me to achieve the same result set without the use of the "distinct" in the select statement? If I don't include the distinct, I get back 16 records, not 4.
The reason you get duplicate rows without DISTINCT is because every row of ID = x will be joined with every other row with ID = x. Since the original table has (1, "Bob") twice, both of those will be joined to every row in the other table with ID = 1.
Removing duplicates before doing a join will do two things: decrease the time to run the query, and prevent duplicate rows from showing up in the result.
Something like (using MySQL version of SQL):
SELECT L.NAME, R.NAME
FROM (SELECT DISTINCT ID, NAME FROM A) AS L
INNER JOIN (SELECT DISTINCT ID, NAME FROM B) AS R
ON L.ID = R.ID
Edit: is B an alias for table A?
In SQL and MY SQL
SELECT COLUMN_NAME FROM TABLE_NAME group by COLUMN_NAME
Have you tried using a group by clause?
select name
from table a
inner join table b
on a.id=b.id
group by name
That should get you the same thing as your distinct query above. As for the result set that you want, a simple self join should do it:
select name1,name2
from(
select id,name as name1
from table
group by 1,2
)a
join(
select id,name as name2
from table
group by 1,2
)b
using(id)
Eliminating duplicate values with union without using distinct
Declare #TableWithDuplicateValue Table(Name Varchar(255))
Insert Into #TableWithDuplicateValue Values('Cat'),('Dog'),('Cat'),('Dog'),('Lion')
Select Name From #TableWithDuplicateValue
union
select null where 1=0
Go
Output
---------
Cat
Dog
Lion
For more alternate kindly visit my blog
http://www.w3hattrick.com/2016/05/getting-distinct-rows-or-value-using.html