MS Access update query reversing table placement in query. Why does it work? - sql

I have a question on SQL execution sequence. I'm using nested iif() conditional statements in MS-Access and because character length became too long, I wanted to use an alias in the statement.
I tried this (by accident) and it works and I'm not sure why and if I should actually use it. Below are shortened examples of the original format compared to the second with enough of the statement to get the gist of it (using generic table names).
I want to update the upDateMe table.
Original pre-alias :
UPDATE upDateMe
INNER JOIN linkMe
ON (linkMe.UniqueID = upDateMe.UniqueID)
AND (linkMe.SrcNumber = upDateMe.SrcNumber)
SET upDateMe.ExpiryDate = [linkMe].[ExpiryDate]
, upDateMe.PermitEnd = [linkMe].[PermitEnd]...
Here I've reversed the tables and put in the alias 'bData':
UPDATE linkMe
INNER JOIN upDateMe AS bData
ON (linkMe.UniqueID = bData.UniqueID)
AND (linkMe.SrcNumber = bData.SrcNumber)
SET bData.ExpiryDate = [linkMe].[ExpiryDate]
, bData.PermitEnd = [linkMe].[PermitEnd]...
This second query works!??. I'm not really sure as to why it would. Can someone explain it??

Because the left side of the set statement is ALWAYS what gets updated.
In code (most maybe not all) you set a variable by setting it = to some value on the right side. The LEFT side is always the target.
In this case, you're telling the database to update the join of linkMe and upDateMe (as bData) setting the bData values to the linkme values. You would likely get an error if you tried to update both bdate and linkMe at the same time as engines generally are only able to update 1 table at a time, since no such conflict seems to exist here, bData is updated without issue.

Related

Update A Record if it exists, else do nothing, including short-cicruit

What I want to do is something that has the following logic:
IF EXISTS (SELECT * FROM people WHERE ID = 168)
THEN UPDATE people SET calculated_value = complex_queries_and_calculations
WHERE ID = 168
.., so to update a field of a given record if that record contains a given data, and else do nothing. To generate the data which would be used for the update, I need to query other tables for values and make some calculations. I want to avoid these queries + calculations, if there's actually nothing to update. And in this case, simply do nothing. Hence, I guess that putting for example an EXIST clause inside a WHERE clause of the UPDATE statement would end in many queries and calculations made in vain.
How can I only UPDATE conditionally and else do nothing, and make sure that all the queries + calculations needed to calculate the value used for the update are only made if the update is needed? And then, in the end, only do the update if complex_queries_and_calculations is not NULL?
My best solution so far uses a Common Table Expression (WITH clause), which makes it impossible to short-circuit. Anyway, such that you can understand the logic I'm trying to achieve, I'm showing what I've been trying so far (without success; code below is not working and I don't know why..):
-- complex queries and calculations; return result as t.result
WITH t AS(complex queries and calculations)
UPDATE target_table
SET
CASE
WHEN t.result IS NOT NULL
THEN target_table.target_column = t.result WHERE target_table.PK = 180
END;
UPDATES (Still saying syntax error, still not working)
WITH t AS(complex_queries_and_calculations AS stamp)
UPDATE target_table
SET target_column =
CASE
WHEN t.stamp IS NULL
THEN target_column
ELSE t.stamp
END
WHERE ID = 168;
Not even this is working (still reporting syntax error on UPDATE line):
WITH t AS(complex_queries_and_calculations AS stamp)
UPDATE target_table
SET target_column = target_column
WHERE ID = 168;
(eventual better approaches which avoid redundant target_column = target_column updates welcome)
With select it works, so I'm totally not understanding the syntax error #1064 it returns for my update query:
WITH t AS(complex_queries_and_calculations AS stamp)
SELECT
CASE
WHEN t.stamp IS NULL
THEN "Error!"
ELSE t.stamp
END
FROM t;
ADDITIONAL INFO
It seems like MariaDB actually does not support CTEs with UPDATE statements; correct me if I'm wrong... So I tried the following:
UPDATE people AS p
INNER JOIN (queries_and_calculations AS result) t
ON p.ID <> t.result -- just to join
SET p.target_column = t.result
WHERE p.ID = 168
AND t.result IS NOT NULL;
and now it's saying:
#4078 - Illegal parameter data types varchar and row for operation '='
Simply do the UPDATE. If there is no row with that ID, it will do nothing. This will probably be no slower than testing first.
Ditto for DELETE when the row might not exist.
"Upsert"/"IODKU" -- INSERT ... ON DUPLICATE KEY UPDATE ... is useful when you want to modify some columns when the row exists (according to some unique column), or add a new row (when it does not exist). This is better than doing a SELECT first.
Think of it this way... A big part of the UPDATE is
opening the table,
locating the block in the table that needs to be modified
loading that block into the cache ("buffer_pool")
All of that is needed for both your SELECT and UPDATE (yeah, redundantly). The UPDATE continues with:
If the row does not exist, exit.
Modify the row, and flag the block as "dirty".
In the background, the block will eventually be flushed to disk.
(I left out details about transactional integrity ("ACID"), etc.)
Even in the worst case, the whole task (for a single row) takes under 10 milliseconds. In the best case, it takes under 1ms and can be done somewhat in parallel with certain other activities.
There is no IF in SQL, since it is not needed:
UPDATE people p
SET calculated_value = c.val
FROM (
SELECT ID, val
FROM
... complex_queries_and_calculations
) c
WHERE c.ID = p.ID
AND ID = 168
AND v.val <> i.val -- maybe add this to avoid idempotent updates. Beware of NULLs, though!
;
~
GOT IT, The following query works to do exactly what I wanted in Mariadb :
UPDATE target_table
LEFT JOIN (complex_queries_and_calculations_to_get_update_value AS update_value) t
ON target_table.ID <> t.update_value -- serves just to have update value in memory,
-- because it needs to be accessed twice to create the updated column value
-- on update, sort of a workaround for CTE + UPDATE in MariaDB
SET target_column = JSON_ARRAY( FORMAT_UPDATE_VALUE(t.update_value),
FORMAT_2_UPDATE_VALUE(t.update_value) )
WHERE ID = 128 AND t.update_value IS NOT NULL;
If the record does not exist, the query takes about 0.0006 secs to execute, without doing anything to the table. If it does exits, it takes 0.0014 secs to execute, while updating the targeted record accordingly. So, it indeed seems to work and resources are saved if the targeted record is not found in target_table. Great thanks to all who helped!

SQL update multiple rows with different values where they match a value from a list

So perhaps the title is a little confusing. If you can suggest better wording for that please let me know and i'll update.
Here's the issue. I've got a table with many thousands of rows and i need to update a few thousand of those many to store latest email data.
For example:
OldEmail#1.com => NewEmail#1.com
OldEmail#2.com => NewEmail#2.com
I've got a list of old emails ('OldEmail#1.com','OldEmail#2.com') and a list of the new ('NewEmail#1.com','NewEmail#2.com'). The HOPE was was to sort of do it simply with something like
UPDATE Table
SET Email = ('NewEmail#1.com','NewEmail#2.com')
WHERE Email = ('OldEmail#1.com','OldEmail#2.com')
I hope that makes sense. Any questions just ask. Thanks!
You could use a case expression:
update mytable
set email = case email
when 'OldEmail#1.com' then 'NewEmail#1.com'
when 'OldEmail#2.com' then 'NewEmail#2.com'
end
where email in ('OldEmail#1.com','OldEmail#2.com')
Or better yet, if you have a large list of values, you might create a table to store them (like myref(old_email, new_email)) and join it in your update query, like so:
update t
set t.email = r.new_email
from mytable t
inner join myref r on r.old_email = t.email
The actual syntax for update/join does vary accross databases - the above SQL Server syntax.
With accuracy to the syntax in particular DBMS:
WITH cte AS (SELECT 'NewEmail#1.com' newvalue, 'OldEmail#1.com' oldvalue
UNION ALL
SELECT 'NewEmail#2.com', 'OldEmail#2.com')
UPDATE table
SET table.email = cte.newvalue
FROM cte
WHERE table.email = cte.oldvalue
or, if CTE is not available,
UPDATE table
SET table.email = cte.newvalue
FROM (SELECT 'NewEmail#1.com' newvalue, 'OldEmail#1.com' oldvalue
UNION ALL
SELECT 'NewEmail#2.com', 'OldEmail#2.com') cte
WHERE table.email = cte.oldvalue
Consider prepared statement for rows update in large batches.
Basically it works as following :
database complies a query pattern you provide the first time, keep the compiled result for current connection (depends on implementation).
then you updates all the rows, by sending shortened label of the prepared function with different parameters in SQL syntax, instead of sending entire UPDATE statement several times for several updates
the database parse the shortened label of the prepared function , which is linked to the pre-compiled result, then perform the updates.
next time when you perform row updates, the database may still use the pre-compiled result and quickly complete the operations (so the first step above can be skipped).
Here is PostgreSQL example of prepare statement, many of SQL databases (e.g. MariaDB,MySQL, Oracle) also support it.

Access 2010 SQL - UPDATE query not working

I need to create a query for updating a column in a table with values taken from another table and matching a field.
These are the 2 tables:
tblMain
ID Autonumbering
Key Text
Stat1 Integer
tblStat1
ID Autonumbering
Key Text
Freq Integer
I want to UPDATE the tblMain.Stat1 column with tblStat1.Freq value on each record in which tblMain.Key = tblStat1.Key.
I tried this syntax (found somewhere as an example)
UPDATE tblMain
SET tblMain.Stat1 = tblStat1.Freq
WHERE tblMain.Key = tblStat1.Key;
This doesn't work and returns an error on the 2nd row.
After some trials I found that the correct syntax (built with the Access query generator) is this:
UPDATE (tblMaibn INNER JOIN tblStat1 ON tblMain.Key = tblStat1.Key)
SET tblMain.Stat1 = tblStat1.Freq;
In this 2nd syntax, there is no trace of the WHERE condition.
Can someone help me to understand what's wrong with the 1st syntax.
Since I'm building a new table (the join), how can it work on tblMain?
As I said, I found the wrong syntax as an example of UPDATE statement.
Thank you in advance.
Bye,
Ivano
What is happening in your first query on the 2nd row, is that Access isn't aware of what tblStat1 represents in your query.
The reason your 2nd query is working is because it uses an inner join on the relevant key. In order for SQL to be aware of what record in tblMain relates to which record in tblStat1, you need to use a join.
You can see in the generated code that it is updating your desired table, but joining onto the second table. The where condition is redundant as you're updating every record.
In 1st syntax, you can change:
UPDATE tblMain
SET tblMain.Stat1 = (SELECT Freq
FROM tblStat1
WHERE tblMain.Key = tblStat1.Key)

Structuring many update statements in SQL Server

I'm using SQL Server. I'm also relatively new to writing SQL... in a strong way. It's mostly self-taught, so I'm probably missing key ideas in terms of proper format.
I've a table called 'SiteResources' and a table called 'ResourceKeys'. SiteResources has an integer that corresponds to the placement of a string ('siteid') and a 'resourceid' which is an integer id that corresponds to 'resourceid' in ResourceKeys. ResourceKeys also contains a string for each key it contains ('resourcemessage'). Basically, these two tables are responsible for representing how strings are stored and displayed on a web page.
The best way to consistently update these two tables, is what? Let's say I have 5000 rows in SiteResources and 1000 rows in ResourceKeys. I could have an excel sheet, or a small program, which generates 5000 singular update statements, like:
update SiteResources set resoruceid = 0
WHERE siteid IS NULL AND resourceid IN (select resourceid
from ResourceKeys where resourcemessage LIKE 'FooBar')
I could have thousands of those singular update statements, with FooBar representing each string in the database I might want to change at once, but isn't there a cleaner way to write such a massive number of update statements? From what I understand, I should be wrapping all of my statements in begin/end/go too, just in-case of failure - which leads me to believe there is a more systematic way of writing these update statements? Is my hunch correct? Or is the way I'm going about this correct / ideal? I could change the structure of my tables, I suppose, or the structure of how I store data - that might simplify things - but let's just say I can't do that, in this instance.
As far as I understand, you just need to update everything in table SiteResources with empty parameter 'placement of a string'. If so, here is the code:
UPDATE a
SET resourceid = 0
FROM SiteResources a
WHERE EXISTS (select * from ResourceKeys b where a.resourceid = b.resourceid)
AND a.siteid IS NULL
For some specific things like 'FooBar'-rows you can add it like this:
UPDATE a
SET resourceid = 0
FROM SiteResources a
WHERE EXISTS (select * from ResourceKeys b where a.resourceid = b.resourceid and b.resourcemessage IN ('FooBar', 'FooBar2', 'FooBar3', ...))
AND a.siteid IS NULL
Let me see if I understood the question correctly. You'd like to update resourceid to 0 if the resourcemessage corresponds to a list of strings ? If so, you can build your query like this.
UPDATE r
SET resourceid = 0
FROM SiteResources r
JOIN ResourceKeys k ON r.resourceid = k.resourceid
WHERE k.resourcemessage IN ('FooBar', ...)
AND r.siteid IS NULL;
This is using an extended UPDATE syntax in transact-sql allowing you to use a JOIN in the UPDATE statement. But maybe it's not exactly what you want ? Why do you use the LIKE operator in your query, without wildcard (%) ?
With table-valued parameters, you can pass a table from your client app to the SQL batch that your app submits for execution. You can use this to pass a list of all the strings you need to update to a single UPDATE that updates all rows at once.
That way you don't have to worry about all of your concerns: the number of updates, transactional atomicitty, error handling. As a bonus, performance will be improved.
I recommend that you do a bit of research what TVPs are and how they are used.

update one table using data from another table

I am trying to update my current table by drawing data from another table.
My database (dbo_finance)
column - test
The other database is assestsc and I am going to pull the data from column issuename1,
however I only want to pull the issuename1 when the field [MinSecClass] is = 9.
This is what I wrote
UPDATE dbo_finance
SET [dbo_finance].cusip9 = AssetsC.cusip
FROM dbo_finance INNER JOIN AssetsC ON dbo_finance.test = AssetsC.[IssueName1]
WHERE (AssetsC.MinSecClass = 9)
Thanks, first time really using SQL
Well I would use aliases, it's a good habit to get into:
UPDATE f
SET [dbo_finance].cusip9 = AssetsC.cusip
FROM dbo_finance f
INNER JOIN AssetsC a ON f.test = a.[IssueName1]
WHERE (a.MinSecClass = 9)
Now that will work fine if the assets table will only return one value for cuspid for each record. If this is a one to many relationship you may need to get more complex to truly get the answer you want.
I see several serious design flaws in your table structure. First joins fields that are dependant as something as inherently unstable as issue name are a very poor choice. You want PK and FK field to be unchanging. Use surrogate keys instead and a unique index.
The fact that you have a field called cusp9 indicates to me that you are denormalizing the data. Do you really need to do this? Do you undestand that this update will have to run in a trigger ever time the cuspid assoicated with MinSecClass changes? Whya re you denormalizing? Do you currently have performance problems? A denormalized table like this can be much more difficult to query when you need data from several of these numbered fields. Since you already have the data in the assets table what are you gaining except a maintenance nightmare by duplicating it?
UPDATE dbo_finance
SET cusip9 = (
SELECT A1.cusip
FROM AssetsC AS A1
WHERE dbo_finance.test = A1.IssueName1
AND AssetsC.MinSecClass = 9
)
WHERE EXISTS (
SELECT *
FROM AssetsC AS A1
WHERE dbo_finance.test = A1.IssueName1
AND A1.MinSecClass = 9
);
Because you're using SQL 2008, you can take advantage of the new(ish) MERGE statement.
MERGE INTO dbo_finance
USING (SELECT IssueName1, cusip FROM AssetsC WHERE MinSecClass = 9) AS source
ON dbo_finance.test = source.IssueName1
WHEN MATCHED THEN UPDATE SET dbo_finance.cusip9 = source.cusip;