I am using the update below to insert district name from the table district into a column in the table motionslisten.
motionslisten contains point data across the five districts in the municipality and also some data outside the municipality. However, the update only enters the name of one district and enters 'Outside municipality' in all other rows.
When I run an update with the st_within but without the case statement, all rows within the municipality are updated with the correct district, so it must be some error with the case statement or the combination of the case statement and st_within.
I could do the update in two separate steps, but as I need the update for a trigger function, I need to make it work.
Why does it produce the wrong result?
update motionslisten m
set district = case
when st_within(m.wkb_geometry::geometry, d.sdo_geometry::geometry)
then d.name
else
'Outside municipality'
end
from district d;
I think you're having the problem because you don't have a join between motionslist and district with your case statement. So, it's evaluating all combinations of points and districts with that st_within, and of course most of them are 'Outside municipality'.
This update will update the rows you expect:
update motionslisten m
set district d.name
from district d where st_within(m.wkb_geometry::geometry, d.sdo_geometry::geometry);
What's the problem with running two updates in a trigger function?
Related
If anyone could even just help me phrase this question better I'd appreciate it.
I have a SQL Server table, let's call it cars, which contains entries representing items and information about their owners including car_id, owner_accountNumber, owner_numCars.
We're using a system that sorts 'importantness of owner' based on number of cars owned, and relies on the owner_numCars column to do so. I'd rather not adjust this, if reasonably possible.
Is there a way I can update owner_numCars per owner_accountNumber using a stored procedure? Maybe some other more efficient way I can accomplish every owner_numCars containing the count of entries per owner_accountNumber?
Right now the only way I can think to do this is to (from the c# application):
SELECT owner_accountNumber, COUNT(*)
FROM mytable
GROUP BY owner_accountNumber;
and then foreach row returned by that query
UPDATE mytable
SET owner_numCars = <count result>
WHERE owner_accountNumber = <accountNumber result>
But this seems wildly inefficient compared to having the server handle the logic and updates.
Edit - Thanks for all the help. I know this isn't really a well set up database, but it's what I have to work with. I appreciate everyone's input and advice.
This solution takes into account that you want to keep the owner_numCars column in the CARs table and that the column should always be accurate in real time.
I'm defining table CARS as a table with attributes about cars including it's current owner. The number of cars owned by the current owner is de-normalized into this table. Say I, LAS, own three cars, then there are three entries in table CARS, as such:
car_id owner_accountNumber owner_numCars
1 LAS1 3
2 LAS1 3
3 LAS1 3
For owner_numCars to be used as an importance factor in a live interface, you'd need to update owner_numCars for every car every time LAS1 sells or buys a car or is removed from or added to a row.
Note you need to update CARS for both the old and new owners. If Sam buys car1, both Sam's and LAS' totals need to be updated.
You can use this procedure to update the rows. This SP is very context sensitive. It needs to be called after rows have been deleted or inserted for the deleted or inserted owner. When an owner is updated, it needs to be called for both the old and new owners.
To update real time as accounts change owners:
create procedure update_car_count
#p_acct nvarchar(50) -- use your actual datatype here
AS
update CARS
set owner_numCars = (select count(*) from CARS where owner_accountNumber = #p_acct)
where owner_accountNumber = #p_acct;
GO
To update all account_owners:
create procedure update_car_count_all
AS
update C
set owner_numCars = (select count(*) from CARS where owner_acctNumber = C.owner_acctNumber)
from CARS C
GO
I think what you need is a View. If you don't know, a View is a virtual table that displays/calculates data from a real table that is continously updated as the table data updates. So if you want to see your table with owner_numCars added you could do:
SELECT a.*, b.owner_numCars
from mytable as a
inner join
(SELECT owner_accountNumber, COUNT(*) as owner_numCars
FROM mytable
GROUP BY owner_accountNumber) as b
on a.owner_accountNumber = b.owner_accountNumber
You'd want to remove the owner_numCars column from the real table since you don't need to actually store that data on each row. If you can't remove it you can replace a.* with an explicit list of all the fields except owner_numCars.
You don't want to run SQL to update this value. What if it doesn't run for a long time? What if someone loads a lot of data and then runs the score and finds a guy that has 100 cars counts as a zero b/c the update didn't run. Data should only live in 1 place, updating has it living in 2. You want a view that pulls this value from the tables as it is needed.
CREATE VIEW vOwnersInfo
AS
SELECT o.*,
ISNULL(c.Cnt,0) AS Cnt
FROM OWNERS o
LEFT JOIN
(SELECT OwnerId,
COUNT(1) AS Cnt
FROM Cars
GROUP BY OwnerId) AS c
ON o.OwnerId = c.OwnerId
There are a lot of ways of doing this. Here is one way using COUNT() OVER window function and an updatable Common Table Expression [CTE]. That you won't have to worry about relating data back, ids etc.
;WITH cteCarCounts AS (
SELECT
owner_accountNumber
,owner_numCars
,NewNumberOfCars = COUNT(*) OVER (PARTITION BY owner_accountNumber)
FROM
MyTable
)
UPDATE cteCarCounts
SET owner_numCars = NewNumberOfCars
However, from a design perspective I would raise the question of whether this value (owner_numCars) should be on this table or on what I assume would be the owner table.
Rominus did make a good point of using a view if you want the data to always reflect the current value. You could also use also do it with a table valued function which could be more performant than a view. But if you are simply showing it then you could simply do something like this:
SELECT
owner_accountNumber
,owner_numCars = COUNT(*) OVER (PARTITION BY owner_accountNumber)
FROM
MyTable
By adding a where clause to either the CTE or the SELECT statement you will effectively limit your dataset and the solution should remain fast. E.g.
WHERE owner_accountNumber = #owner_accountNumber
how to check if value from external table is different than current table then update.
I am updating student firstname from firstname of extstudent. if value is different then only update else dont update
Update student
SET FirstName = FirstName
FROM ExtStudent
You need to join the two tables in your UPDATE (Transact-SQL) statement so that you can compare the two. Both tables have to have the same key so that you can match up the records in your table to your "external" table. In my example below, I'm going to assume that the primary key of both tables is a field called StudentId.
Update Student
SET Student.FirstName = ExtStudent.FirstName
FROM Student
JOIN ExtStudent
ON Student.StudentID = ExtStudent.StudentID
Where Student.FirstName <> ExtStudent.FirstName
This will update the Student table and set the FirstName field to the matching record with the same StudentID in the ExtStudent table where the FirstName fields do not match. Because this is an inner join, it will not update if no matching record is not found.
If you are updating multiple fields and want to update the record if any of the fields changed, then check for inequality for each field in the where clause separated by an OR.
You could technically leave off the where clause because if it doesn't change it would just be updating it to the same value. However keep in mind that this does register as an update, so any update triggers would be fired for all the records... even the ones that didn't change.
Another issue you might run into is if your name fields are nullable. The where clause will return false if either one of the fields are null, so it would not update. You could account for this with the isnull function.
Where isnull(Student.FirstName,'') <> isnull(ExtStudent.FirstName,'')
If changed to a left join with this modification, it would update FirstName to NULL if no match was found.
I often find myself with a need to select a value from one of two different tables, tableA and tableB. If a certain record exists in tableA, I grab the value from tableA. If the record does not exist, I instead need a value from tableB.
I am working with an ERP application that will create an event based record in a sales order ship-to address table if the user changes the default ship-to address when entering a sales order. If the user does not change the ship-to address, the system assumes the product is being shipped to the address on the customer file and does not write anything to the sales order ship-to table. I need the ship-to address for every sales order shipment, but I am not safe in grabbing it directly from either tableA or tableB.
What I've done in the past is something like this:
SELECT
CASE
WHEN isnull(tableA.SalesOrderNumber, 'StackOverflow') = 'StackOverflow'
THEN tableB.address1
ELSE tableA.address1
END AS address1
Which works. But then you end up repeating this same thing over and over again for every field with similar logic, which sucks, but it is not unworkable if the value you are checking for (SalesOrderNumber) is character based data and/or a primary key. If the value I am checking for is not a primary key or if it is numeric type data then this approach becomes much more fragile.
There's got to be a better way, right?
I have tried a few different approaches with the EXISTS operator and gotten nowhere.
If I understand your task you can left join your tableA to check for existence of particular record - if record doesn't exist you will have NULL for joined values, otherwise you'll have some value. Then in case NULL value you can replace it with a value from tableB. Something like this:
SELECT COALESCE(tA.address1, tB.address1, N'N/A') AS address1
FROM tableWithSalesOrderNumbers AS tWSON
LEFT JOIN tableA AS tA
ON tA.SalesOrderNumber = tWSON.SalesOrderNumber
LEFT JOIN tableB AS tB
ON tB.SalesOrderNumber = tWSON.SalesOrderNumber
I'm working on a view which is then updated by the user. This update basically changes the value of column. But right now it doesnt let me do that and produces this :
Update or insert of view or function '' failed because it contains a derived or constant field.
I know this is because I have a constant in the select statement but is there a way to get around it? Please help
This is my code for the view
Create view Schema.View1
as
SELECT
Convert(Varchar(20),l.jtpName) as JobType, Convert(Varchar(10),' <All> ')as SubCategory , Convert(varchar (3), Case when a.jtpName= l.jtpName and a.subName= ' <All> ' then 'Yes' else 'No' end) As AutoProcess from Schema.JobType l left join Schema.Table1 a on l.jtpName=a.jtpName
UNION
SELECT
Convert(Varchar(20),a.jtpName) as JobType, Convert(Varchar(10),a.subName) as SubCategory, Convert(varchar (3),Case when b.jtpName= a.jtpName and b.subName= a.subName then 'Yes' else 'No' end) As AutoProcess from Schema.SubCategory a left join fds.Table1 b on a.subName=b.subName
GO
Finally the update statement:
UPDATE Schema.View1 SET AUTOPROCESS = Case WHEN AUTOPROCESS = 'Yes' Then 'No' END Where JOBTYPE = 'Transport' and SUBCATEGORY= 'Cargo'
Thank You
You cannot update a column that is the result of a computation.
According to MSDN, one of the conditions for a view column to be updatable is this:
Any modifications, including UPDATE, INSERT, and DELETE statements, must reference columns from only one base table.
The columns being modified in the view must directly reference the underlying data in the table columns. The columns cannot be derived in any other way, such as through the following:
An aggregate function: AVG, COUNT, SUM, MIN, MAX, GROUPING, STDEV, STDEVP, VAR, and VARP.
A computation. The column cannot be computed from an expression that uses other columns. Columns that are formed by using the set operators UNION, UNION ALL, CROSSJOIN, EXCEPT, and INTERSECT amount to a computation and are also not updatable.
The columns being modified are not affected by GROUP BY, HAVING, or DISTINCT clauses.
TOP is not used anywhere in the select_statement of the view together with the WITH CHECK OPTION clause.
Here not only does your view uses the UNION statement, the AutoProcess field you are trying to update is actually the result of a CASE statement that uses two fields. It makes no sense to try and update that.
I would recommend that you use stored proc to perform writing operations. Or, as Damien suggest, you could use an INSTEAD OF trigger on the view too.
You have to create a TRIGGER and manually apply the changes from the inserted and deleted pseudo-tables against the base tables yourself.
There is no way for sql server to work backwards from your convert functions to the original fields. You cannot update a view this way.
If the view contained your jptName and subName fields, you might be able to update just those fields.
This is probably a very simple question for you SQL folks out there.
I have a temp table (TMP_VALIDATION_DATA) in which I've stored the old and new values of some fields I wish to update in a production table (PROVIDER_SERVICE), plus the uuids of the PROVIDER_SERVICE records that need to be updated.
What I want to accomplish is this, in pseudo-code:
For every prov_svc_uuid uuid in TMP_VALIDATION_DATA table
Set PROVIDER_SERVICE_RATE.END_DATE = NewPvSvcEndDate
Where [uuid in temp table] = [uuid in PROVIDER_SERVICE table]
end for
Is this Update statement going to accomplish what I need?
update PROVIDER_SERVICE
set END_DATE = (
select NewPvSvcEndDate
from TMP_VALIDATION_DATA T
where T.PROVIDER_SERVICE_UUID = PROVIDER_SERVICE.PROVIDER_SERVICE_UUID
)
If my UPDATE is incorrect, will you please provide the correction? Thanks.
Your query will update all records and you might get an error if you have more than one record in your subquery. I would also change your syntax to a JOIN similar to below.
update P
set END_DATE = T.NewPvSvcEndDate
FROM PROVIDER_SERVICE P
JOIN TMP_VALIDATION_DATA T
ON P.PROVIDER_SERVICE_UUID = T.PROVIDER_SERVICE_UUID
If you don't want to UPDATE all records, then add a WHERE clause.
My suggestion is if you don't know how many records would be included in the UPDATE, write your query as a SELECT first, then change it to an UPDATE. So for this one:
SELECT P.END_DATE, T.NewPvSvcEndDate
FROM PROVIDER_SERVICE P
JOIN TMP_VALIDATION_DATA T
ON P.PROVIDER_SERVICE_UUID = T.PROVIDER_SERVICE_UUID
This will either update all records, or error out (not sure what happens when you try to update a column with multiple values like that).