How to populate a table based on a value from a different table - sql

I have two tables of data which I can join using a left join linked on the ID in both tables. Where the course and the person are the same, I need to populate the RegNumber as the same as the RegNumber which is already there for 1 row:
How it is currently: if I join table 1 and table 2 with a left join.
Table 1
ID | Course| Person
67705 | A | 1
68521 | A | 1
85742 | A | 1
89625 | A | 1
67857 | B | 2
86694 | B | 2
88075 | B | 2
88710 | C | 3
47924 | C | 3
66981 | C | 3
12311 | B | 1
12312 | B | 1
12313 | B | 1
Table 2
ID | RegNumber
67705 | N712316
NULL | NULL
NULL | NULL
NULL | NULL
67857 | N712338
NULL | NULL
NULL | NULL
NULL | NULL
47924 | M481035
NULL | NULL
12311 | N645525
NULL | NULL
NULL | NULL
I need table 2 to look like this:
ID | RegNumber
67705 | N712316
68521 | N712316
85742 | N712316
89625 | N712316
67857 | N712338
86694 | N712338
88075 | N712338
88710 | N712338
47924 | M481035
66981 | M481035
12311 | N645525
12312 | N645525
12313 | N645525
That is, I need to insert new rows into Table 2
Can anyone help me please? This is Totally beyond my capability!

insert into table2 (ID,RegNumber)
select t1.ID,reg.regNumber
from table1 t1
cross join (select top 1 regNumber from table2 r2 join table1 r1
on r1.Id = r2.Id
and r1.Course = t1.Course
and r1.Person = t1.person
order by id) reg
where not exists (select 1 from table2 t2 where t1.ID = t2.ID)
you can improve performance a little bit by loading data into temp table first :
select t1.ID , Course,Person,regNumber
into #LoadedData
from table1 t1
join table2 t2 on t1.Id = t2.ID
insert into table2 (ID,RegNumber)
select t1.ID,reg.regNumber
from table1 t1
cross join (select top 1 regNumber from #LoadedData l
where l.Course = t1.Course
and l.Person = t1.person
order by id) reg
where not exists (select 1 from #LoadedData l where t1.ID = l.ID)
in either case having an index on (ID, Course, Person) will help with performance

Assuming:
You are missing items in table 2 that inherit data from other records in table 1.
What makes two different IDs share the same Regnumber is to have BOTH course and person number in common.
You really need to join table 1 to itself to create the mapping that associates ID 67705 with ID 68521, then you can join in table 2 to pick up the Regnumber.
Try this:
Insert into table2 (ID,RegNumber)
Select right1.ID, left2.RegNumber
From (
(table2 left2 INNER JOIN
table1 left1 On (left1.ID=left2.ID)
INNER JOIN table1 right1 On (left1.Course=right1.Course AND left1.Person=right1.Person)
) LEFT OUTER JOIN table2 right2 On (right1.ID=right2.ID)
WHERE right2.ID Is Null
The 4th table join (alias right2) is purely defensive, to handle two records in table2 having identical Person & Course in table1.

I have solved this myself.
I concatenated the person and course columns and then joined them using that new concatenated field
insert into table 2 (ID,RegNumber)
select X1.ID,X2.Regnumber
from (select concat(course,person) as X,ID from table1) X1
join (select concat(t1.course,t1.person) as X, t2.RegNumber
from table1 t1
join table2 t2 on t1.ID = t2.ID) X2
on X1.X = X2.X
where X1.ID not in (select ID from table2)

Related

Join tables with same keys, second table has multiple values for key and rows of second table must have same column value

I have two tables with shared key and I'm trying to join them to filter data based on few conditions
tbl1
id | OutPutValue |
1 | 2019 |
2 | 2018 |
tbl2
object_id | status | type |
1 | 22 | a |
1 | 22 | c |
1 | 33 | b |
2 | 33 | c |
2 | 33 | c |
2 | 33 | c |
What I'm trying to get is : it must select all 'OutPutValue' from tbl1 where, in tbl2 column 'type' should be c, and column 'status' must have same value for all rows i.e. 33. Note that Primary key (id) of tbl1 is foreign key (object_id) in tbl2.
Select column from tbl1 if, All rows in tbl2 (id of tbl1 have multiple rows (based on object_id) in tbl2) have same status value i.e. 33 and Type should be 'c'.
OutPutValue | Type | status |
2018 | c | 33 |
I have tried with following solution, but it's not returning desired output :
SELECT a.OutPutValue FROM tbl1 a JOIN tbl2 b ON a.id = b.object_id WHERE b.Type =c
GROUP BY a.OutPutValue, b.status HAVING b.STATUS IN(33)
You can try using correlated subquery
DEMO
select distinct OutPutValue,type, status
from t2 a inner join t1 b on a.object_id=b.id
where type='c' and not exists
(select 1 from t2 a1 where a.object_id=a1.object_id and status<>33 and type='c')
OUTPUT:
OutPutValue type status
2018 c 33
Another solution could be the following :
SELECT T1.id, T1.outputvalue FROM tbl1 T1
JOIN (
SELECT tbl2.*, MAX(type), MAX(status)
FROM tbl2
GROUP BY object_id
HAVING
MIN(status) = MAX(status) AND
MIN(type) = MAX(type)
) T2 ON T1.id = T2.object_id
WHERE T2.type = 'c'
EDIT: I have updated my query to match a particular case which make it quite similar to another answer.
FIND A DEMO HERE
Try a join combined with an aggregation:
SELECT
t1.OutPutValue,
MAX(t2.type) AS type,
MAX(t2.status) AS status
FROM tbl1 t1
INNER JOIN tbl2 t2
ON t1.id = t2.object_id
GROUP BY
t1.id,
t1.OutPutValue
HAVING
MIN(t2.status) = MAX(t2.status) AND
MAX(t2.status) = 33 AND
MIN(t2.type) = MAX(t2.type) AND
MAX(t2.type) = 'c';

How to do an outer join with full result between two tables

I have two tables:
TABLE1
id_attr
-------
1
2
3
TABLE2
id | id_attr | val
----------------------
10 | 1 | A
10 | 2 | B
As a result I want a table that show:
RESULT
id | id_attr | val
----------------------
10 | 1 | A
10 | 2 | B
10 | 3 | NULL
So I want the row with id=10 and id_attr=3 also when id_Attr=3 is missing in TABLE2 (and I know that because I have a NULL value (or something else) in the val column of RESULT.
NB: I could have others ids in table2. For example, after insert this row on table2: {11,1,A}, as RESULT I want:
id | id_attr | val
----------------------
10 | 1 | A
10 | 2 | B
10 | 3 | NULL
11 | 1 | A
11 | 2 | NULL
11 | 3 | NULL
So, for every id, I want always the match with all id_attr.
Your specific example only has one id, so you can use the following:
select t2.id, t2.id_attr, t2.val
from table2 t2
union all
select 10, t1.id_attr, NULL
from table1 t1
where not exists (select 1 from table2 t2 where t2.id_attr = t1.id_attr);
EDIT:
You can get all combinations of attributes and ids in the following way. Use a cross join to create all the rows you want and then a left join to bring in the data you want:
select i.id, t1.id_attr, t2.val
from (select distinct id from table2) i cross join
table1 t1 left join
table2 t2
on t2.id = i.id and t2.id_attr = t1.id_attr;
It sounds like you want to do just an outer join on id_attr instead of id.
select * from table2 t2
left outer join table1 t1 on t2.id_attr = t1.id_attr;

What is the correct way from performance perspective to match(replace) every value in every row in temp table using SQL Server 2016 or 2017?

I am wondering what should I use in SQL Server 2016 or 2017 (CTE, LOOP, JOINS, CURSOR, REPLACE, etc) to match (replace) every value in every row in temp table? What is the best solution from performance perspective?
Source Table
|id |id2|
| 1 | 2 |
| 2 | 1 |
| 1 | 1 |
| 2 | 2 |
Mapping Table
|id |newid|
| 1 | 3 |
| 2 | 4 |
Expected result
|id |id2|
| 3 | 4 |
| 4 | 3 |
| 3 | 3 |
| 4 | 4 |
You may join the second table to the first table twice:
WITH cte AS (
SELECT
t1.id AS id_old,
t1.id2 AS id2_old,
t2a.newid AS id_new,
t2b.newid AS id2_new
FROM table1 t1
LEFT JOIN table2 t2a
ON t1.id = t2a.id
LEFT JOIN table2 t2b
ON t1.id2 = t2b.id
)
UPDATE cte
SET
id_old = id_new,
id2_old = id2_new;
Demo
Not sure if you want just a select here, or maybe an update, or an insert into another table. In any case, the core logic I gave above should work for all these cases.
You'd need to apply joins on update query. Something like this:
Update tblA set column1 = 'something', column2 = 'something'
from actualName tblA
inner join MappingTable tblB
on tblA.ID = tblB.ID
this query will compare eachrow with ids and if matched then it will update/replace the value of the column as you desire. :)
Do the self join only
SELECT t1.id2 as id, t2.id2
FROM table1 t
INNER JOIN table2 t1 on t1.id = t.id
INNER JOIN table2 t2 on t2.id = t.id2
This may have best performance from solutions posted here if you have indexes set appropriately:
select (select [newid] from MappingTable where id = [ST].[id]) [id],
(select [newid] from MappingTable where id = [ST].[id2]) [id2]
from SourecTable [ST]

Postgresql COALESCE does not set default value

I have two tables:
tcars
id | name | car_price
----|---------------------|------------
1 |First_car_name | 1000
2 |Second_car_name | 1200
tcar_optionals
id | id_car | spec | opt_included |price
----|----------|------|-------------------------
1 | 2 |Spec1 | true | 500
2 | 2 |Spec2 | true | 100
3 | 2 |Spec3 | false | 500
4 | 2 |Spec4 | true | 0
5 | 1 |Spec5 | false | 500
6 | 1 |Spec6 | true | 0
And the following query:
select t1.id, coalesce(t1.car_price, 0)+ coalesce(sum(t2.price), 0) as total_price
from tcars t1
left join tcar_optionals t2 on t2.id_car = t1.id
where t2.opt_included and t2.price>0 and t1.id=?
group by t1.id, t1.car_price
It returns the id from tcars and the total_price(car_price+price of included optionals that have price>0).
Example:
for t1.id=2 returns:
id | total_price
----|------------
2 | 1800
The problem appears when I have no included optionals with price>0, for example t1.id = 1.
What it returns:
id | total_price
----|------------
What I need is return only t1.car_price as total_price if there are no included optionals with price>0:
id | total_price
----|------------
1 | 1000
Can someone help me with this problem, please?
You should firstly join the tables with all conditions on the second table and aggregate values from this (joined) result, e.g:
select id, coalesce(car_price, 0)+ coalesce(sum(price), 0) total_price
from tcars
left join tcar_optionals on id = id_car and spec_included
-- where id = 1
group by id, car_price
The condition q1.id_car=1 in the where clause effectively turns your outer join into an inner join because for rows not matching the join condition q1.id_car will be null and the comparison =1 will remove those rows again.
You would need to put that into the JOIN condition - but as you already have a condition on the id_car in the derived table ("q1"), you don't need it anyway.
The other possibility would be to filter on the corresponding value from the tcars table: where t1.id = 1
Edit
By moving the conditions on the t2 table to the join condition you do get what you want:
select t1.id, coalesce(t1.car_price, 0) + coalesce(sum(t2.price), 0) as total_price
from tcars t1
left join tcar_optionals t2
on t2.id_car = t1.id
and t2.opt_included and t2.price > 0 --<< conditions for tcar_optionals go here
where t1.id = 1 --<< limit the car you want to see
group by t1.id;
If id is defined as the primary key in tcars, then group by t1.id is enough.
See the example here: http://rextester.com/YOYF30261
select (t1.car_price + coalesce(extra_price, 0)) as start_price
from tcars t1
left join (select id_car,sum(price) as extra_price from tcar_optionals
where opt_included and price > 0 group by 1) q1 on q1.id_car = t1.id
where t1.id=$1

MySQL: Typecasting NULL to 0

Let us suppose the following table (e.g. a result of several inner join statements):
id | column_1 | column_2
------------------------
1 | 1 |
2 | 2 | 2
3 | | 3
Which you could for example get from the following statement:
select a.id, t1.column_1, t2.column_2
from a
left join t1 on a.id = t1.id
left join t2 on a.id = t2.id
Now, if i'd like to sum up t1.column_1 and t2.column_2 as follows
select
a.id,
t1.column_1,
t2.column_2,
(t1.column_1 + t2.column_2) as cumulated
from a
left join t1 on a.id = t1.id
left join t2 on a.id = t2.id
The result will look as follows:
id | column_1 | column_2 | cumulated
------------------------------------
1 | 1 | NULL | NULL
2 | 2 | 2 | 4
3 | NULL | 3 | NULL
My question basically is: is there a way to typecast NULL into 0 in order to do some math?
I have tried CONVERT(t1.column_1, SIGNED) and CAST(t1.column_1 as SIGNED), but a NULL stays a NULL.
Use IFNULL(column, 0) to convert the column value to zero.
Alternatively, the COALESCE function will do the same thing: COALESCE(column, 0), except
COALESCE is ANSI-compliant, IFNULL is not
COALESCE takes an arbitrary number of columns/values and will return the first non-null value passed to it.