Use SELECT inside an UPDATE query - sql

How can I UPDATE a field of a table with the result of a SELECT query in Microsoft Access 2007.
Here's the Select Query:
SELECT Min(TAX.Tax_Code) AS MinOfTax_Code
FROM TAX, FUNCTIONS
WHERE (((FUNCTIONS.Func_Pure)<=[Tax_ToPrice]) AND ((FUNCTIONS.Func_Year)=[Tax_Year]))
GROUP BY FUNCTIONS.Func_ID;
And here's the Update Query:
UPDATE FUNCTIONS
SET FUNCTIONS.Func_TaxRef = [Result of Select query]

Well, it looks like Access can't do aggregates in UPDATE queries. But it can do aggregates in SELECT queries. So create a query with a definition like:
SELECT func_id, min(tax_code) as MinOfTax_Code
FROM Functions
INNER JOIN Tax
ON (Functions.Func_Year = Tax.Tax_Year)
AND (Functions.Func_Pure <= Tax.Tax_ToPrice)
GROUP BY Func_Id
And save it as YourQuery. Now we have to work around another Access restriction. UPDATE queries can't operate on queries, but they can operate on multiple tables. So let's turn the query into a table with a Make Table query:
SELECT YourQuery.*
INTO MinOfTax_Code
FROM YourQuery
This stores the content of the view in a table called MinOfTax_Code. Now you can do an UPDATE query:
UPDATE MinOfTax_Code
INNER JOIN Functions ON MinOfTax_Code.func_id = Functions.Func_ID
SET Functions.Func_TaxRef = [MinOfTax_Code].[MinOfTax_Code]
Doing SQL in Access is a bit of a stretch, I'd look into Sql Server Express Edition for your project!

I wrote about some of the limitations of correlated subqueries in Access/JET SQL a while back, and noted the syntax for joining multiple tables for SQL UPDATEs. Based on that info and some quick testing, I don't believe there's any way to do what you want with Access/JET in a single SQL UPDATE statement. If you could, the statement would read something like this:
UPDATE FUNCTIONS A
INNER JOIN (
SELECT AA.Func_ID, Min(BB.Tax_Code) AS MinOfTax_Code
FROM TAX BB, FUNCTIONS AA
WHERE AA.Func_Pure<=BB.Tax_ToPrice AND AA.Func_Year= BB.Tax_Year
GROUP BY AA.Func_ID
) B
ON B.Func_ID = A.Func_ID
SET A.Func_TaxRef = B.MinOfTax_Code
Alternatively, Access/JET will sometimes let you get away with saving a subquery as a separate query and then joining it in the UPDATE statement in a more traditional way. So, for instance, if we saved the SELECT subquery above as a separate query named FUNCTIONS_TAX, then the UPDATE statement would be:
UPDATE FUNCTIONS
INNER JOIN FUNCTIONS_TAX
ON FUNCTIONS.Func_ID = FUNCTIONS_TAX.Func_ID
SET FUNCTIONS.Func_TaxRef = FUNCTIONS_TAX.MinOfTax_Code
However, this still doesn't work.
I believe the only way you will make this work is to move the selection and aggregation of the minimum Tax_Code value out-of-band. You could do this with a VBA function, or more easily using the Access DLookup function. Save the GROUP BY subquery above to a separate query named FUNCTIONS_TAX and rewrite the UPDATE statement as:
UPDATE FUNCTIONS
SET Func_TaxRef = DLookup(
"MinOfTax_Code",
"FUNCTIONS_TAX",
"Func_ID = '" & Func_ID & "'"
)
Note that the DLookup function prevents this query from being used outside of Access, for instance via JET OLEDB. Also, the performance of this approach can be pretty terrible depending on how many rows you're targeting, as the subquery is being executed for each FUNCTIONS row (because, of course, it is no longer correlated, which is the whole point in order for it to work).
Good luck!

I had a similar problem. I wanted to find a string in one column and put that value in another column in the same table. The select statement below finds the text inside the parens.
When I created the query in Access I selected all fields. On the SQL view for that query, I replaced the mytable.myfield for the field I wanted to have the value from inside the parens with
SELECT Left(Right(OtherField,Len(OtherField)-InStr((OtherField),"(")),
Len(Right(OtherField,Len(OtherField)-InStr((OtherField),"(")))-1)
I ran a make table query. The make table query has all the fields with the above substitution and ends with INTO NameofNewTable FROM mytable

Does this work? Untested but should get the point across.
UPDATE FUNCTIONS
SET Func_TaxRef =
(
SELECT Min(TAX.Tax_Code) AS MinOfTax_Code
FROM TAX, FUNCTIONS F1
WHERE F1.Func_Pure <= [Tax_ToPrice]
AND F1.Func_Year=[Tax_Year]
AND F1.Func_ID = FUNCTIONS.Func_ID
GROUP BY F1.Func_ID;
)
Basically for each row in FUNCTIONS, the subquery determines the minimum current tax code and sets FUNCTIONS.Func_TaxRef to that value. This is assuming that FUNCTIONS.Func_ID is a Primary or Unique key.

I did want to add one more answer that utilizes a VBA function, but it does get the job done in one SQL statement. Though, it can be slow.
UPDATE FUNCTIONS
SET FUNCTIONS.Func_TaxRef = DLookUp("MinOfTax_Code", "SELECT
FUNCTIONS.Func_ID,Min(TAX.Tax_Code) AS MinOfTax_Code
FROM TAX, FUNCTIONS
WHERE (((FUNCTIONS.Func_Pure)<=[Tax_ToPrice]) AND ((FUNCTIONS.Func_Year)=[Tax_Year]))
GROUP BY FUNCTIONS.Func_ID;", "FUNCTIONS.Func_ID=" & Func_ID)

I know this topic is old, but I thought I could add something to it.
I could not make an Update with Select query work using SQL in MS Access 2010. I used Tomalak's suggestion to make this work. I had a screenshot, but am apparently too much of a newb on this site to be able to post it.
I was able to do this using the Query Design tool, but even as I was looking at a confirmed successful update query, Access was not able to show me the SQL that made it happen. So I could not make this work with SQL code alone.
I created and saved my select query as a separate query. In the Query Design tool, I added the table I'm trying to update the the select query I had saved (I put the unique key in the select query so it had a link between them). Just as Tomalak had suggested, I changed the Query Type to Update. I then just had to choose the fields (and designate the table) I was trying to update. In the "Update To" fields, I typed in the name of the fields from the select query I had brought in.
This format was successful and updated the original table.

Related

How to debug in the middle of a SELECT query?

I'm debugging a SELECT query with correlated subquery that isn't returning the right results. The form of my query is, schematically
SELECT id, cost, country
FROM table AS table1
WHERE table1.cost =
(
SELECT MAX(table2.cost)
FROM table as table2
WHERE table2.country = table1.country
)
I'd like to be able to pause mid-execution and see the values being compared in the WHERE statement. However a PRINT statement seems not to run if I put it as the first line of the subquery, for example. I am new to SQL and would be curious what a more experienced person considers to be the most time-efficient way to debug in such a situation.
SQL does not support what you want want to do.
It is very important to remember that SQL is a descriptive language not a procedural language. A SQL query describes the result set being produced. It does not specify the specific actions being taken.
If you want to know what the result of the subquery is, then run that as a separate query.

In Access create a calculated column called order based on a column of dates

in Microsoft Access I need to bind a cross-tab query to a report. To bind the query I need to know the column names in advance which is the problem as I do not know which dates will be in the data. The date names end up as the columns in the cross-tab query. as the dates vary the column names vary and I can't bind the report as I don't know the column names. the best solution I can think of is to calculate a column that replaces the date with its order. then the column names will always be 1,2,3,4,5,6,7,8. However I haven't been able to create this calculated order column. No matter what I do access bugs out. Part of the cause is that This table will be used for a cross-tab query with parameters so access bugs out with any additional complications.
How do I turn StatusDate into CalculatedOrder?
Edit:
I cannot use a subquery to make CalculatedOrder as is sujested in the comments because the original query has parameters. access bugs out when a subquery draws on a query based on parameters.
The real problem was two bugs in Access. My guess is Access will bug at any additional complexity. if the original table has parameters work with a copy of the table that doesn't have parameters. In all cases, before calculating the crosstab, copy just your values into another temporary table. In Access, crosstab doesn't work if a crosstab column variable is a correlated subquery.
You can make the CalculatedOrder column using a correlated subquery similar to allenbrowne.com/ranking.html.
if the table has parameters:
PARAMETERS myfirstparameter
SELECT StatusDate, CrossTabRow, CrossTabValue INTO NoParametersTable
FROM MyTableWithParameters
WHERE ...
Then make a query with only a grouped StatusDate.
SELECT NoParametersTable.StatusDate
FROM NoParametersTable
GROUP BY NoParametersTable.StatusDate;
Then Turn StatusDate into Order using a correlatedsubquery:
SELECT CrossTabRow, CrossTabValue, (SELECT Count(dupe.StatusDate) +1 FROM [MyGroupedStatusDateQuery] as dupe WHERE dupe.StatusDate < [MyGroupedStatusDateQuery].StatusDate) AS [Order]
FROM NoParametersTable INNER JOIN [MyGroupedStatusDateQuery] ON NoParametersTable.StatusDate = MyGroupedStatusDateQuery.StatusDate)
finally make sure to turn this final query into a table without a correlatedsubquery by copying the values into another temporary table. run the crosstab on the final temporary table and the crosstab will work because the final table just has values instead of parameters and a correlated subquery.

Is there a way to include a query that is non updateable in an UPDATE query? [duplicate]

This question already has an answer here:
Access SQL Update One Table In Join Based on Value in Same Table
(1 answer)
Closed 10 years ago.
For the following query:
UPDATE tempSpring_ASN AS t
SET t.RECORD_TYPE = (
SELECT TOP 1 RECORD_TYPE
FROM (
SELECT "A" AS RECORD_TYPE
FROM TABLE5
UNION ALL
SELECT "B" AS RECORD_TYPE
FROM TABLE5
)
);
I'm getting, "Operation must use an updateable query." I don't understand. I'm not trying to update a union query. I'm just trying to update an otherwise updatable recordset with the output (single value) of a union query.
(The solution provided at Access SQL Update One Table In Join Based on Value in Same Table (which is also provided below) does not work for this situation, contrary to what is indicated on the top of this page.)
This question is a reference to a previous question, data and code examples posted here:
Access SQL Update One Table In Join Based on Value in Same Table
Hi AYS,
In Access, an Update query needs to be run on a table.
As a UNION query is a combination of multiple sets of records, the result set is no longer a table, and cannot be the object of an Update query as the records in the result set are no longer uniquely identified with any one particular table (even if they theoretically could be). Access is hard-coded to treat every UNION query as read-only, which makes sense when there are multiple underlying tables. There are a number of other conditions (such as a sub-query in the SELECT statement) that also trigger this condition.
Think if it this way: if you were not using TOP 1 and your UNION query returned multiple results, how would JET know which result to apply to the unique record in your table? As such, JET treats all such cases the same.
Unfortunately, this is the case even when all of the data is being derived from the same table. In this case, it is likely that the JET optimizer is simply not smart enough to realize that this is the case and re-phrase the query in a manner that does not use UNION.
In this case, you can still get what you want by re-stating your query in such a way that everything references your base table. For example, you can use the following as a SELECT query to get the PO_NUM value of the previous SHP_CUSTOM_5 record:
SELECT
t1.SHP_CUSTOM_5
, t1.PO_NUM
, t1.SHP_CUSTOM_5 -1 AS PREV_RECORD
, (SELECT
t2.PO_NUM
FROM
tempSpring_ASN As t2
WHERE
t2.SHP_CUSTOM_5 = (t1.SHP_CUSTOM_5 -1)
) AS PREV_PO
FROM
tempSpring_ASN AS t1
;
You can then phrase this as an Update query as follows in order to perform the "LIN" updates:
UPDATE
tempSpring_ASN AS t1
SET
t1.RECORD_TYPE = "LIN"
WHERE
t1.PO_NUM=
(
SELECT
t2.PO_NUM
FROM
tempSpring_ASN As t2
WHERE
t2.SHP_CUSTOM_5 = (t1.SHP_CUSTOM_5 -1)
)
;
This code was successful in the tests I ran with dummy data.
Regarding your "HDR" updates, your are really performing two separate updates.
1) If the PO_NUM matches the previous record's PO_NUM, set RECORD_TYPE to "LIN"
2) If it is the first record, set RECORD_TYPE to "HDR"
It is not clear to me why there would be a benefit to performing these actions within one query. I would recommend performing the HDR update using the "TOP 1" by SHP_CUSTOM_5 method you used in your original SELECT query example, as this will be a relatively simple UPDATE query. It is possible to use IIF() within an Update query, but I do not know what additional benefit you would gain from the additional time and complexity that would be required (it would most likely only be much less readable).
Best of luck!

SQL Server New column added to table but problems with Select *

Morning,
I have a stored procedure that returns 'SELECT *' from a table.
Whenever I add a new column to the table, the 'SELECT *' often returns some data in the wrong columns.
Is this an optimization or caching problem? How do I solve this without having to explicitly define the return column names in my stored procedure?
Thanks!
Regardless of the exact nature of your problem, or a solution to it, I would recommend that you don't use Select * From Table. This is less efficient - each time you run the query, an extra request is sent to the DB to determine exactly what columns '*' constitutes, and then a proper request is sent with specific column information.
The reason for your problem probably depends on the client stack you're using.
Speaking in very general terms, SQL Server will return the columns in the order they're added to the table if you perform a SELECT *. If there is more than one table involved in the query, it will return the columns from each table in the order they appear in the query.
Neither caching nor optimisation should affect this server-side if the table columns have changed, so it may be something happening between your code and the server, in whatever data access stack you happen to be using.
This is one of the reasons it's generally recommended not to use "SELECT *" in client code.
The best way to avoid this problem is to have the stored procedure return a predefined set of columns (SELECT a, b instead of SELECT *) and use a table join to retrieve the rest of the code. Because stored procedures cannot be part of a query, you could refactor the stored procedure into a table-valued user-defined function and perform a join on it:
SELECT f.a, f.b, t.*
FROM dbo.fn_YourFunction('a', 'b') f
INNER JOIN YourTable t ON f.id = t.id

VB6 SQL 2005 Database Index Question

I have a VB app that accesses a sql database. I think it’s running slow, and I thought maybe I didn’t have the tables propery indexed. I was wondering how you would create the indexes? Here’s the situation.
My main loop is
Select * from Docrec
Order by YearFiled,DocNumb
Inside this loop I have two others databases hits.
Select * from Names
Where YearFiled = DocRec.YearFiled
and Volume = DocRec.Volume and Page = DocRec.Page
Order by SeqNumb
Select * from MapRec
Where FiledYear = DocRec.YearFiled
and Volume = DocRec.Volume and Page = DocRec.Page
Order by SeqNumb
Hopefully I made sense.
Try in one query using INNER JOIN:
SELECT * FROM Doctec d
INNER JOIN Names n ON d.YearField = n.YearField AND d.Volume = n.Volume AND d.Page = n.Page
INNER JOIN MapRec m ON m.FiledYear = n.YearFiled AND m.Volume = n.Volumen and m.Page = n.Page
ORDER BY YearFiled, DocNumb
You will have only one query to database. The problem can be that you hit database many times and get only one (or few) row(s) per time.
Off the top, one thing that would help would be determining if you really need all columns.
If you don't, instead of SELECT *, select just the columns you need - that way you're not pulling as much data.
If you do, then from SQL Server Management Studio (or whatever you use to manage the SQL Server) you'll need to look at what is indexed and what isn't. The columns you tend to search on the most would be your first candidates for an index.
Addendum
Now that I've seen your edit, it may help to look at why you're doing the queries the way you are, and see if there isn't a way to consolidate it down to one query. Without more context I'd just be guessing at more optimal queries.
In general looping through records is a poor idea. can you not do a set-based query that gives you everything you need in one pass?
As far as indexing consider any fields that you use in the ordering or where clauses and any fileds that arein joins. Primary keys are indexed as part of the setup of a primary ley but foreign keys are not. Often people forget that they need to index them as well.
Never use select * in a production environment. It is a poor practice. Do not ever return more data than you need.
I don't know if you need the loop. If all you are doing is grabbing the records in maprec that match for docrec and then the same for the second table then you can do this without a loop using inner join syntax.
select columnlist from maprec m inner join docrec d on (m.filedyear = d.yearfield and m.volume = d.volume and m.page=d.page)
and then again for the second table...
You could also trim up your queries to return only the columns needed instead of returning all if possible. This should help performance.
To create an index by yourself in SQL Server 2005, go to the design of the table and select the Manage Indexes & Keys toolbar item.
You can use the Database Engine Tuning Advisor. You can create a trace (using sql server profiler) of your queries and then the Advisor will tell you and create the indexes needed to optimize for your query executions.
UPDATE SINCE YOUR FIRST COMMENT TO ME:
You can still do this by running the first query then the second and third without a loop as I have shown above. Here's the trick. I am thinking you need to tie the first to the second and third one hence why you did a loop.
It's been a while since I have done VB6 recordsets BUT I do recall the ability to filter the recordset once returned from the DB. So, in this case, you could keep your loop but instead of calling SQL every time in the loop you would simply filter the resulting recordset data based on the first record. You would initialize / load the second & third query before this loop to load the data. Using the syntax above that I gave will load in each of those tables the matching to the parent table (docrec).
With this, you will still only hit the DB three times but still retain the loop you need to have the parent docrec table traversed so you can do work on it AND the child tables when you do have a match.
Here's a few links on ado recordset filtering....
http://www.devguru.com/technologies/ado/QuickRef/recordset_filter.html
http://msdn.microsoft.com/en-us/library/ee275540(BTS.10).aspx
http://www.w3schools.com/ado/prop_rs_filter.asp
With all this said.... I have this strange feeling that perhaps it could be solved with just a left join on your tables?
select * from docrec d
left join maprec m on (d.YearFiled= m.FiledYear and d.Volume = m.Volume and d.Page = m.Page)
left join names n on (d.YearFiled = n.YearFiled and d.Volume = n.Volume and d.Page = n.Page)
this will return all DocRec records AND add all the maprec values and name values where it matches OR NULL if not.
If this fits your need it will only hit the DB once.