Why can't I use the field name as a parameter in an Access parameter query? - sql

Working through some insert/update queries on an application today and came across a result I hadn't expected.
Queries
My insert queries look similar to this:
PARAMETERS nm TEXT(10), st TEXT(2);
INSERT INTO park(pname, pstate)
VALUES([nm],[st]);
And their companion updates were like this:
PARAMETERS id LONG, nm TEXT(10), st TEXT(2);
UPDATE park
SET
pname = [nm], pstate = [st]
WHERE
ID = [id];
The table they were updating was similar to this:
park
ID LONG | pname TEXT(10) | pstate TEXT(2)
Unexpected Result
Working through writing the queries, I tested each by running it against the database and providing test values for the various parameters. After the insert query, I'd test the update by updating the newly inserted record.
In most cases the tables were empty, so the update would simply update the single record in place.
However as soon as I ran the update on a previously populated table, I found the query was attempting to update ALL records, not just the one whose ID was being provided through the parameter.
The question is why??

Problem
While ID certainly was a field in the park table, using id as a parameter was essentially stating the following in the WHERE clause:
WHERE ID = id;
or
WHERE ID = ID;
Since ID is always equal to itself, the UPDATE attempted to update ALL records instead of only the expected record with the provided ID.
Solution
To fix the problem, I simply used the first and last letter of the table being updated before the id for each case I was updating a record identified by its ID. So the working code - that updates only the record identified - is:
PARAMETERS pkid LONG, nm TEXT(10), st TEXT(2);
UPDATE park
SET
pname = [nm], pstate = [st]
WHERE
ID = [pkid];
Obviously I wasn't paying close attention while I was writing the query -- while I'd used different names for other parameters, I didn't do so when it came to the ID for the update query.
Bottom Line
Whenever working with paramaterized queries, make sure your parameter names DO NOT match your table's field names.
Doing so will avoid the issue noted above as well as other related issues.
You'll also want to avoid reserved words for your table, field, and parameter names.
Hope this helps someone avoid a possibly nasty surprise when updating records -- or trying to figure out why their parameter queries didn't seem to work.

Related

Dealing with multiple output results in UPSERT query [SQL]

I'm trying to do an update query in which a single row the table is updated and, if nothing has matched and updated, a new row is inserted. In each case, I need the query to return the ID of the inserted row.
The issue I'm having is this query is returning 2 separate results when the insert case is reached, one for each output (the first empty, the second containing the ID). I'm running this query using SQL Alchemy on python and I'm only able to see the first result, which is empty.
UPDATE [Rights]
SET accessLevel = :access_level
OUTPUT inserted.rightsID
WHERE principal = :principal and [function] = :function
IF ##ROWCOUNT = 0
INSERT INTO Rights(principal, [function], accessLevel)
OUTPUT inserted.rightsID
VALUES(:principal, :function, :access_level)
And I'm calling it like so:
inserted_right_id = session.execute(sql_rights_update, right).fetchall()
Can anyone recommend a way of changing the query so that I can still use the UPSERT method, but only receive one of the IDs? I was considering storing the OUTPUT values into a table and returning that, or wrapping the whole thing in a select but hopefully there's something more elegant out there.
Thanks a million.
Feeling quite dumb. I simply added a
IF EXISTS(SELECT * FROM Rights WHERE principal = :principal and [function] = :function)
UPDATE ...
ELSE
INSERT ...

MS Access query with parameter from combobox in form does not return any results

I have created an MS Access database with several tables and queries but the problem described further down is about the following:
tEmployee table contains employee data as the name implies.
tCases table contains data about court cases.
Each case can be assigned to only one employee. I have created a relationship from [tCases]![assignedTo] field to tEmployee.
fSearches is a form to perform searches among the cases. It includes a combobox cEmployee which is populated from [tEmployee].[surname] and a command button to perform the search.
GOAL: to select employee in [fSearches]![cEmployee], hit the button and show all the cases assigned to this employee in another form named fResultsCases.
This is the code for the button where lines in comments are some of the things I've tried:
If cEmploee.ListIndex = -1 Then
MsgBox "You have to select employee to perform search.", title:="Missing value!"
Else
tHidden2.SetFocus
tHidden2.Text = "assigned to " & cEmploee & "."
DoCmd.OpenForm "fResultsCases"
'Forms("fResultsCases").RecordSource = "SELECT * FROM tCases WHERE [tCases]![assignedTo] like [Forms]![fSearches]![cEmploee];"
'Forms("fResultsCases").RecordSource = "SELECT * FROM tCases WHERE tCases .[assignedTo] = [Forms]![fSearches]![cEmploee];"
Forms("fResultsCases").RecordSource = "SELECT * FROM tCases WHERE [assignedTo] = [Forms]![fSearches]![cEmploee];"
Forms("fResultsCases").Recalc
End If
With all the above mentioned I get the following:
[fResultsCases] opens but does not return any case.
I tried to omit the WHERE clause and [fResultsCases] returns all cases as expected.
Then I tried to narrow things down to understand the problem by creating a simple query with one parameter and got exactly the same results.
SELECT tCases.[case number], tCases.subject, tCases.fromDt, tCases.toDt,
tCases.assignedTo, tCases.[date assigned], tCases.[date completed]
FROM tCases
WHERE (((tCases.assignedTo)=[Forms]![fSearches]![cEmploee]));
It seems like I'm missing something about the WHERE clause when it comes to combobox values but I can't figure it out. I am new to MS Access. Any help will be very appreciated.
UPDATE:
cEmploee rowsource property: SELECT [tEmployee].surname FROM tEmployee ORDER BY [surname];
cEmploee bound column property: 1
I cannot recreate your issue replicating your setup with combox that triggers an update of form's recordsource. And because neither an error is raised nor parameter input prompt appears, likely the WHERE clause is returning False (i.e., no matching records) and hence no rows are returned.
The reason can likely be due to the combobox [Forms]![fSearches]![cEmploee] which show Employee's surname but is bound to the unique ID as the default setup of Access' comboboxes (if using wizard). See Data tab of combobox's properties in Design View.
The reason for this hidden bound field is users never know the unique ID of a record but do know the value (i.e., name). So when they select the recognized name they are really selecting the corresponding unique ID. Therefore, consider adjusting WHERE condition accordingly where ID is the unique table ID field which matches the bound field of combobox.
Forms("fResultsCases").RecordSource = "SELECT * FROM tCases WHERE [ID] = [Forms]![fSearches]![cEmploee];"
Forms("fResultsCases").Recalc
Forms("fResultsCases").Form.Requery ' USUALLY THE RECORDSOURCE UPDATE CALL
After about ten days of "struggle" with this brainteaser, I decided to post this question to ask for help. Fortunatelly #Parfait has involved with my problem and with his/her accurate remarks helped me understand my mistakes. It also helped me Ken Sheridan's post here. So I am posting my answer just for future reference. #Parfait, I sincerely thank you for your time.
So the problem in this case was primarily understanding how relationships between tables affect data types. If you want to connect a textfield of tableA to tableB, then there are two cases. If the primary key in tableB is number then textfield becomes number. If the primary key in tableB is text then textfield remains text.
In my case it was the first one. Specifically the field [tCases]![assignedTo] after creating relationship with table [tEmployee], was turned automatically into number (It still shows the surname but contains the corresponding primary key in tEmployee). On the contrary combobox [cEmploee] still contained text. This why this line:
Forms("fResultsCases").RecordSource = "SELECT * FROM tCases WHERE [assignedTo] = [Forms]![fSearches]![cEmploee];"
was not returning any record as #Parfait successfully guessed.
At this point there were two possible solutions:
1) Set surname as primary key to agree with combobox contents.
delete the relationship between [tCases]![assignedTo] and [tEmployee].
set field [tEmployee]![surname] (which is text) as primary key in table [tEmployee].
create a new relationship between [tCases]![assignedTo] and [tEmployee].
I did not try this solution though, as a problem would occur if 2 employees had the same surname. So I suggest following the second.
2) Keep the same primary key and use it to match the records from tableA to the surname from tableB.
To achieve this you have to inform access, that you want the records from tableA that have the ID, while in tableB this ID corresponds to the surname inserted in the combobox (hope this makes sense). In other words create a recordset of both tables (union query). For this purpose I replaced the above refered line of code with the following:
Forms("fResultsCases").RecordSource = "SELECT tCases.* FROM tCases INNER JOIN tEmployee ON tCases.[assignedTo] = tEmployee.[employeeID] " & _
"WHERE tEmployee.[surname]=[Forms]![fSearches]![cEmploee] AND tCases![assignedTo]=[tEmployee]![employeeID];"
Hope this saves some time from other people. Have a nice day!

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)

SQLite changes() counts non-changing UPDATEs

I have question regarding SQLite's changes() function, which, according to the documentation, "returns the number of database rows that were changed or inserted or deleted by the most recently completed INSERT, DELETE, or UPDATE statement" (also see the documentation of the underlying C/C++ function).
I was hoping to use this function to check whether the execution of an UPDATE statement pertaining to a single row has really caused that row to be changed or not.
By changed I do not just mean that the row matched the statement's WHERE clause. No, instead what I mean is that, for the row in question, the value of at least 1 column is actually different after the execution compared to before. If you ask me this is the only proper definition of a change in this context.
So I was hoping to detect such changes by checking whether changes() returns 1 (row changed) or 0 (row unchanged) when called right after the execution of the UPDATE statement.
But much to my despair this does not seem to work as expected.
Allow me to illustrate:
CREATE TABLE People (Id INTEGER PRIMARY KEY AUTOINCREMENT, Name TEXT NOT NULL);
INSERT INTO People (Name) VALUES ("Astrid");
SELECT changes();
Here changes() returns 1, as expected because we just INSERTed 1 row.
UPDATE People SET Name = "Emma" WHERE Id = 1;
SELECT changes();
Here changes() returns 1, as expected because 1 row was UPDATEd (i.e. actually changed: the Name of the Person with Id = 1 was "Astrid" but is now "Emma").
UPDATE People SET Name = "John" WHERE Id = 200;
SELECT changes();
Here changes() returns 0, as expected because there is no row with Id = 200.
So far so good. But now have a look at the following UPDATE statement, which does indeed match an existing row, but does not actually change it at all (Name remains set to "Emma")...
UPDATE People SET Name = "Emma" WHERE Id = 1;
SELECT changes();
Here changes() returns 1, while I was of course hoping for 0 :-(.
Perhaps this would have made sense if the function was called something like matched_rows() or affected_rows(). But for a function called changes(), and documented as it is, this behaviour strikes me as illogical, or confusing at best.
So anyway, can somebody explain why this happens, or, even better, suggest an alternative strategy to achieve my goal in a reliable (and efficient) way?
All I can think of is to actually do something like SELECT * FROM People WHERE Id = x, compare all returned column values with the values I'm about to set in the UPDATE statement and thereby decide whether I need to execute the UPDATE at all. But that can't be very efficient, right?
Of course in this toy example it might not matter much, but in my actual application I'm dealing with tables with many more columns, some of which are (potentially big) BLOBs.
The database does not compare old and new values; any UPDATEd row always counts as "changed" even if the values happen to be the same.
The documentation says that
the UPDATE affects … those rows for which the result of evaluating the WHERE clause expression as a boolean expression is true.
If you want to check the old value, you have to do it explicitly:
UPDATE People SET Name = 'Emma' WHERE Id = 1 AND Name IS NOT 'Emma';

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.