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? - sql

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]

Related

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

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)

Update a table based on a condition

I have a column Table1.Tradedate and another column Table2.SettlementDate.
Based on the comparison between these 2, I want to update a column in table 2
IF (Table1.TradeDate <= Table2.SettlementDate)
BEGIN
UPDATE Table2 SET Status='Y'
END
This is what I have tried but I know its wrong, since the table will obviously contain more than 1 records. So, I believe what I should do is
use a join on 2 tables based on some #id to pick a particular record
check the IF condition for that particular record
update the Status column in table2.
I hope my approach is correct but I am writing it incorrectly.
Table1:
SKacc | Name | TradeDate | Othercolumns....
1 | xxx | 01/07/2019 |
2 | xxx | 01/06/2019 |
Table2:
SKAcc | Name | SettlementDate | Status |Other Columns....
1 | xxx | 01/08/2019 | NULL |
2 | xxx | 01/08/2019 | NULL |
Try below
update t2 set Status = 'Y'
from table2 t2
join table1 t1 on t1.id = t2.id
where t1.tradeDate <= t2.settlementDate
Try joining the two tables with the related column and then update the table you want to update with the value. Using inner join in the example but can change depending on the usecase
UPDATE Table2
SET Status = 'Y'
FROM Table2
INNER JOIN Table1 ON Table1.id = Table2.table1_id
WHERE Table1.TradeDate <= Table2.SettlementDate
I would not recommend a JOIN for this purpose. Instead:
update table2
set Status = 'Y'
where exists (select 1
from table1 t1
where t1.id = t2.id and
t1.tradeDate <= t2.settlementDate
);
The reason I recommend this version is because you have not specified that id is unique in table1. In general, you only want to use JOIN in UPDATE when you can guarantee that there is only one matching row.

How do you exclude another table from a query in SQL Server?

Let's assume you have the following two SQL Server tables:
t1:
|----------------------------------------------|
|name | day_planed | day_canceled | discription|
|----------------------------------------------|
|alpha| 2015-11-02 | 2015-11-01 | some |
|alpha| 2015-11-02 | 2015-10-30 | text |
|beta | 2015-11-02 | 2015-11-01 | here |
|----------------------------------------------|
t2:
|----------------------------------------------|
|name | day_planed | day_canceled | discription|
|----------------------------------------------|
|alpha| 2015-11-02 | 2015-10-30 | text |
|----------------------------------------------|
In an query of t1 I now want every entry except those of t2. I already tried something similar to
SELECT *
FROM t1 AS A
LEFT JOIN t2 as B ON (A.name = B.name
AND A.day_planed = B.day_planed
AND A.day_canceled != B.day_canceled)
Unfortunately I don't get it why it does not exclude the row from t2 in the query of t1.
A second question would be, if there is actual an easy way to make a query of t1 without t2 by just returning the row with the maximum description. I tried looking into it in SQL Server, but could only find the first identifier, which does not work for this "wonderful" implementation of sql...
There are a few ways to do it in this "wonderful" implementation.
SELECT * FROM t1
EXCEPT
SELECT * FROM t2
is one. Another is:
SELECT *
FROM t1
WHERE NOT EXISTS
( SELECT *
FROM t2
WHERE t2.name = t1.name
AND t2.day_planed = t1.day_planed
AND t2.day_canceled = t1.day_canceled
)
Or you could use LEFT JOIN and check for rows that didn't match with WHERE t2.name IS NULL after your ON clause. Like what you have but with = instead of !=
SELECT t1.*
FROM t1
LEFT JOIN t2
ON t2.name = t1.name
AND t2.day_planed = t1.day_planed
AND t2.day_canceled = t1.day_canceled
WHERE t2.name IS NULL;
If you want to check every column (including discription), go with EXCEPT.
You are just missing a where clause and a condition on discription. You also need to change != to =.
SELECT *
FROM t1 AS A
LEFT JOIN t2 as B ON (A.name = B.name
AND A.day_planed = B.day_planed
AND A.day_canceled = B.day_canceled
AND A.discription = B.discription)
WHERE B.Name IS NULL

Joining two sql tables with a one to many relationship, but want the max of the second table

I am trying to join two tables one is a unique feature the seconds is readings taken on several dates that relate to the unique features. I want all of the records in the first table plus the most recent reading. I was able to get the results I was looking for before adding the shape field. By using the code
SELECT
Table1.Name, Table1.ID, Table1.Shape,
Max(Table2.DATE) as Date
FROM
Table1
LEFT OUTER JOIN
Table2 ON Table1.ID = table2.ID
GROUP BY
Table1.Name, Table1.ID, Table1.Shape
The shape field is a geometry type and I get the error
'The type "Geometry" is not comparable. It can not be use in the Group By Clause'
So I need to go about it a different way, but not sure how.
Below is a sample of the two tables and the desired results.
Table1
Name| ID |Shape
AA1 | 1 | X
BA2 | 2 | Y
CA1 | 3 | Z
CA2 | 4 | Q
Table2
ID | Date
1 | 5/27/2013
1 | 6/27/2014
2 | 5/27/2013
2 | 6/27/2014
3 | 5/27/2013
3 | 6/27/2014
My Desired Result is
Name| ID |Shape |Date
AA1 | 1 | X | 6/27/2014
BA2 | 2 | Y | 6/27/2014
CA1 | 3 | Z | 6/27/2014
CA2 | 4 | Q | Null
You can do the aggregation on Table2 in a CTE, finding the MAX(DATE) for each ID, and then join that result to Table1:
WITH AggregatedTable2(ID, MaxDate) AS
(
SELECT
ID, MAX(DATE)
FROM
Table2
GROUP BY
ID
)
SELECT
t1.ID, t1.Name, t1.Shape, t2.MaxDate
FROM
Table1 t1
LEFT JOIN
AggregatedTable2 t2 ON t1.ID = t2.ID
Try casting geometry as a varchar.
Select Table1.Name, Table1.ID, cast(Table1.Shape as varchar(1)) AS Shape, Max(Table2.DATE) as Date
FROM Table1 LEFT OUTER JOIN
Table2 ON Table1.ID = table2.ID
Group By Table1.Name, Table1.ID, cast(Table1.Shape as varchar(1))
Try this:
SELECT t1.Name
, t1.ID
, t1.Shape
, MAX(t2.Date) As Date
FROM Table1 AS t1
LEFT JOIN Table2 AS t2
ON t2.ID = t1.ID
GROUP
BY t1.Name
, t1.ID
, t1.Shape

Database to return multi-dimensional result

This is a follow-up question to my previous one.
Situation:
Table 1:
+--------------------+--------------------+
| v_id | v_name |
+--------------------+--------------------+
| 1 | v_name1 |
+--------------------+--------------------+
| etc...
Table 2:
+--------------------+--------------------+
| a_id | a_name |
+--------------------+--------------------+
| 1 | a_name1 |
+--------------------+--------------------+
| etc...
Table 3:
+--------------------+--------------------+
| v_id | a_id |
+--------------------+--------------------+
| 1 | 1 |
+--------------------+--------------------+
| 1 | 2 |
+--------------------+--------------------+
| 1 | 3 |
+--------------------+--------------------+
I want to get a join of the two tables:
SELECT t1.*, t2.a_name
FROM `table1` t1
LEFT JOIN `table_3` t3 ON t3.v_id = t1.v_id
LEFT JOIN `table_2` t2 ON t2.a_id = t3.a_id
WHERE t1.id = 1;
The result of this query will be 3 rows for the given tables, each row with the same values from Table 1, just a different a_name from Table 3.
Now, is it possible for me to make a_name an array (of 3 cells in this case)? I think that this is not possible. If it's not, how would I have to build the query so that it would return just one row, with the a_name results joined with a given 'glue' string?
EDIT
If I were to get a result in PHP, I would expect something like:
$result = array(
'v_id' => 1,
'a_name' => array('a_name1', 'a_name2', 'a_name3')
);
or
$result = array(
'v_id' => 1,
'a_name' =>'a_name1, a_name2, a_name3'), # assuming I used ', ' as the glue string
);
I believe you are looking for the GROUP_CONCAT function.
In your query it would look like this:
SELECT t1.*, GROUP_CONCAT(t2.a_name SEPARATOR ',')
FROM `table1` t1
LEFT JOIN `table_3` t3 ON t3.v_id = t1.v_id
LEFT JOIN `table_2` t2 ON t2.a_id = t3.a_id
WHERE t1.id = 1
GROUP BY [list of t1 columns here]
-- update thanks to OMG Ponies.
Use:
SELECT t1.*,
GROUP_CONCAT(DISTINCT t2.a_name SEPARATOR ',')
FROM `table1` t1
LEFT JOIN `table_3` t3 ON t3.v_id = t1.v_id
LEFT JOIN `table_2` t2 ON t2.a_id = t3.a_id
WHERE t1.id = 1
GROUP BY [list of t1 columns here]
Sean Viera's answer works as you pointed out because of the WHERE clause, and because MySQL allows for columns to be omitted from the GROUP BY. It's not a good habit - only MySQL allows this, and such queries will not port to other databases (there's lots of examples on SO).
I added the distinct in the GROUP_CONCAT - didn't think you want duplicates if any exist.