easiest way to map ids during database refactoring - sql

i have a number of tables with a column called OrderId. I have just done a refactoring and i want to get rid of the Order table and i have a new table called Transaction. I want all tables that have an OrderId column to now have a TransactionId column
This is complete. I now need to populate the transactionId column. I have a mapping today between orderId and transactionId so i wanted to see the quickest way i can go populate that new transactionId column (should i do this through code, through a SQL query, etc ??)
So i have the transationId column in the Order Table so i can do a join.
I want a query that says something like this (pseudo SQL)
update childTable CT
set transactionId = MapFromOrderId(CT.OrderId)
any suggestions?

I would do it in SQL code:
UPDATE MT
SET
transaction_id = MAP.transaction_id
FROM
My_Table MT
INNER JOIN My_Map MAP ON
MAP.order_id = MT.order_id
Then check to make sure that every row was mapped:
SELECT
*
FROM
My_Table
WHERE
transaction_id IS NULL

The process is usually:
Make sure the database is backed up
Addtransctionid to each child table.
Populate based on a join to the
mapping table (you did store the
mappings between orderid and
transactionid in a table?)
Make sure you have no blank values.
Then you create the FK for
transactions, drop the fk to the
Order table and then drop the orderid
column.
Then move to the next table and
repeat.
Test to make sure everything worked
properly
Definitely I'd do this in a script so it will be easy to port to prod after dev and QA testing.
On prod you need to do this while the database is in single user mode to prevent new orders from being added as the process transitions.

Related

one-to-one tables relationship, linked by the autonumber in the main table

I have a main table that contain the customers details, I built another table that contain for example a yes/no fields about if the customer paid his taxes, the two tables is linked with autonumber from the main table.
I want always to keep them both with the same amount of records (that means every customer has a record in the second table even if the second table has empty record with data only in the primary key field)
I need that cause with missing records I cannot run update query to auto fill the second table and i got an error of validation rule violation.
I use this sql:
update clients LEFT JOIN MonthlyTbl ON clients.SerialNo = MonthlyTbl.serialno
set sReport04='ready';
I have almost 700 records in the main table and only 80 records in the second, and when I run the sql it updates only 80!!!!
Thanks for Help
Please use below query,
update clients set sReport04='ready' where SerialNo in
(select serialno from MonthlyTbl);
here is the right answer
first run the sql:
INSERT INTO monthlytbl ( serialno )
SELECT clients.serialno FROM clients
WHERE (((clients.[serialno]) Not In (select serialno from monthlytbl)));
and then:
select sreport04 from monthlytbl
set sReport04='ready';

SQL - Selecting a field from another table using a primary key in a trigger

I have two tables in my database, one is Transactions and the other is TransactionHistories. The latter is essentially an auditing table, whereby a trigger executes on insert, update and delete on Transactions to capture a screenshot of the data.
I am successfully retrieving all of the data stored in the Transactions table where the columns match, but the difficulty comes where I am trying to retrieve data from another table using a foreign key. For instance:
The transaction table has a field "TransactionType_TransactionTypeId", but in the audit table we wish to store its 'name' equivalent as "TransactionTypeName". This needs to be populated from the "TransactionTypes" table, which has the fields "TransactionTypeId" and "Name".
I am struggling to write a query to retrieve this as we wish. I am trying something similar to the following but having little success:
SELECT #TransactionTypeName=Name
FROM TransactionTypes
WHERE inserted.TransactionType_TransactionTypeId=TransactionTypes.TransactionTypeId;
I'm assuming that is a syntactic nightmare. If someone could point me in the right direction I would be extremely grateful!
well to get a name you should do the following
select #TransactionTypeName = TT.Name
from inserted as i
left outer join TransactionTypes as TT on TT.TransactionTypeId = i.TransactionType_TransactionTypeId
but you have to know that inserted table can have more than one row, and you are getting value for only one row.

Fastest way to modify each row in a table

What's the recommended way of updating a relatively large table (~70 million rows), in order to replace a foreign key column with an id of a different table (indirectly linked by the current key)?
Let's say I have three tables:
Person
Id long,
Group_id long --> foreign key to Group table
Group
Id long
Device_id long --> foreign key to Device table
Device
Id long
I would like to update the Person table to have a direct foreign key to the Device table, i.e.:
Person
Id long,
Device_Id long --> foreign key to Device table
Device
Id long
The query would look something like this:
-- replace Group_id with Device_id
update p from Person p
inner join Group g
on g.Id = p.Group_id
set p.Group_id = g.Device_id
I would first drop the FK constraint, and then rename the column afterwards.
Will this work?
Is there a better way?
Can I speed it up? (while this query is running, everything else will be offline, server is UPS backed-up, so I'd like to skip any transactional updates)
It would work if you wrote the UPDATE properly (assuming this is SQL Server)
update p
set p.Group_id = g.Device_id
from Person p
inner join Group g on g.Id = p.Group_id
Apart from that, it's a really smart move to re-use, then rename the column*. Can't think of any smart way to make this any faster, unless you wish to use a WHILE loop and person.Id markers to break up the updates into batches.
* - ALTER TABLE DROP COLUMN DOES NOT RECLAIM THE SPACE THE COLUMN TOOK
Drop indexes on the table you are updating and recreate after the update is complete.
Drop constraints on the table you are updating and recreate appropriately (you are changing the reference after all) after the update is complete.
Turn off triggers on the table you are updating and enable after the update is complete.
You might want to consider running batches. I personally would create a loop and batch update 10k rows at a time. This seemed to cause the fewest problems on my hardware (running out of disk space, etc). You could order the update and track the PK so you know where you are at. Or create a bit column that is set when a particular record is updated; this method might make it easier overall as you won't need to track the PK at all.
An example of such a loop might look like this:
select top 1 * from table
DECLARE #MinPK BIGINT
DECLARE #MaxPK BIGINT
SET #MinPK=0
SET #MaxPK=0
WHILE ##ROWCOUNT>0
BEGIN
SELECT
#MaxPK=MAX(a.PK)
FROM (
SELECT TOP 3
PK
FROM Table
WHERE PK>#MinPK
ORDER BY PK ASC
) a
--Change this to an update
SELECT
PK
FROM Table
WHERE PK>#MinPK
AND PK<=#MaxPK
SET #MinPK=#MaxPK
END
Your idea won't "work", unless there is only one device per group (which would be ridiculous, so I assume not).
The problem is that you would have to cram many device_id values into one column in the person table - that's why you've got a group table in the first place.

Avoid CASE clause while importing data to SQL Server

I have to import data from an old schema to a new one, where a column 'career_name' (in table 'users') that used to be a VARCHAR now should be an INTEGER which is a foreign key to another table careers. This way I intend to tipify the data that was stored in the VARCHAR column in order to keep integrity in my database.
Actually, I do the following:
Create both new tables in the new schema
Use SELECT DISTINCT 'career_name' FROM old_table in order to obtain all possible values
INSERT into my new table careers the rows obtained above
INSERT data into my new table users using a CASE clause in order to obtain the ID from table careers of the corresponding former career_name so the relation is created.
Now the problem is the following, the table careers is big, so writing one CASE clause for each row in the table in order to import users is nearly impossible.
Is there a way to avoid this? I wish I could use my table careers as an associative array, where the key was the former career_name and the value the row's ID... Thanks!
INSERT into new_Users
SELECT Users.Name, careers.id
FROM Users inner join careers ON Users.career_name = careers.career_name
Because the schema isnt known to me, I am assuming that new_users table has 2 columns which is name and career_id.
Is there a way to avoid this? I wish I could use my table 'careers' as an associative array, where the key was the former 'career_name' and the value the row's ID... Thanks!
Er... That's what a table is, isn't it? Just join to your new careers table to use it as a lookup when doing the insert:
INSERT INTO users (blah, whatever, career_id)
SELECT
old_users_table.blah,
old_users_table.whatever,
careers.career_id
FROM
old_users_table INNER JOIN careers ON old_users_table.career_name = careers.career_name
... where users is your new users table, old_users_table is wherever you're getting the data you want to migrate, and careers is your new careers table.

How to order the resulting query information being inserting into a table while leaving existing table records on top?

I am trying to insert values from a table into another existing table and have just the values I am inserting be sorted in descending order based on a specific column while leaving the existing records at the top of the table. How do I do that? I have tried to use an Order By statement but whether I use the column name of the table I'm pulling from or the destination table's column name I get an error. Also this is being run in VBA using DoCmd.RunSQL.
Here is my existing query:
INSERT INTO AllMetersAvgRSSI
(longitude,latitude,AvgRSSI)
Select
Prem.longitude, Prem.latitude,
DataByColl.[Avg RSSI]
From [Prem]
Left
Join DataByColl ON (Prem.meter_miu_id
= DataByColl.[MIU ID])
Order BY [AvgRSSI] desc
Final Result
I continued to fiddle with this and discovered than you can use an order by just like I have shown above to do exactly as I was trying to do. The problem I was apparently having was caused by the names of the column I wanted sorted being changed only from Avg RSSI to AvgRSSI. When I changed the destination table to have the same field name as the source table it orders the incoming information while leaving the existing information alone. I also did a test where I changed the name of the destination table to AverageRSSI and it worked the same way. So in the end it was the names of the fields being differed only by a space that was causing the problem. The final Query is:
INSERT INTO AllMetersAvgRSSI
(longitude,latitude,[Avg RSSI])
Select
Prem.longitude, Prem.latitude,
DataByColl.[Avg RSSI]
From [Prem]
Left
Join DataByColl ON (Prem.meter_miu_id
= DataByColl.[MIU ID])
Order BY [Avg RSSI] desc
Ordering in an INSERT makes no sense from a database standpoint. How the database puts the rows into a table depends on the underlying physical structure of the table, not the order in which they are inserted.
Maybe your application relies on an auto incrementing column being in a certain order which would then be dependent on the order of insertion, but if that's the case then I would say that you've made a mistake in your database design as there shouldn't be business logic designed around an auto incrementing column.
Remove the ORDER BY from your INSERT statement and if you need to retrieve rows in a particular order later then use an ORDER BY there.
Create a temp table, add the first result set in the desired order. Insert your new values into the table, query the table to return your new results with an order by into your temp table, select your temp table the results will be in the order you added them unless you do another order by.
Don't forget to drop your temp table after displaying the results.