Ambiguous column name on update of new column used in view - sql

Scenario
Adding a column to a table and then updating that column
alter sometable add example_column_name varchar(255);
update sometable set example_column_name = '';
(real update is a bit more complex, but this is a boiled down version we used trying to find the problem)
Problem
The update query gives 'Ambiguous column name example_column_name.'
This works in all databases except one.
It is only for exactly one specific column name it happens, adding a column with different name and updating that column works
The column name in question works in other databases, and it already exists in other tables in the same db
Question
Does anyone know what's going on, how can we get past this problem?
Update
The problem was an indexed view that used the column name of the new column in an already existing query. See comments and accepted answer for details.

This error can't happen purely from the code shown.
There must be a trigger or indexed view in play. You have ruled out triggers so an example demonstrating the indexed view scenario is below
CREATE TABLE T1(X INT, Y INT)
CREATE TABLE T2(X INT, Z INT)
GO
CREATE VIEW V1
WITH SCHEMABINDING
AS
SELECT T1.X,
T1.Y,
Z
FROM dbo.T1
JOIN dbo.T2
ON T1.X = T2.X
GO
CREATE UNIQUE CLUSTERED INDEX IX
ON V1(X)
GO
ALTER TABLE T1
ADD Z INT;
GO
UPDATE T1
SET Z = 0
When the view is initially created the only table containing a column Z is T2 so it is not ambiguous. After adding column Z to T1 the view definition becomes ambiguous. The UPDATE to the table tries to automatically maintain the view and the error is thrown.
Msg 209, Level 16, State 1, Procedure V1, Line 5 [Batch Start Line 23]
Ambiguous column name 'Z'. Msg 4413, Level 16, State 1, Line 25 Could
not use view or function 'V1' because of binding errors.
It is best practice to always use two part naming where your query references more than one table to avoid this type of error.

Related

OUTPUT INTO fails due to invalid columns name

I am trying to make two inserts one after another like:
INSERT INTO tbl_tours (TimeFrom)
OUTPUT inserted.tourId, DispatchingId, TimeFrom, TimeTo INTO tbl_tourData (tour_fk, dispatchId, timeFrom, timeTo)
SELECT TimeFrom
FROM #tmpTable
SELECT * FROM tbl_tours
SELECT * FROM tbl_tourData
But I get an error:
Msg 207 Level 16 State 1 Line 13
Invalid column name 'DispatchingId'.
Msg 207 Level 16 State 1 Line 13
Invalid column name 'TimeFrom'.
Msg 207 Level 16 State 1 Line 13
Invalid column name 'TimeTo'.
You can check full code at this fiddle:
https://dbfiddle.uk/?rdbms=sqlserver_2016&fiddle=c10f9886bcfb709503007f18b24eabfd
How to combine these inserts?
The output clause can only refer to columns that are inserted. So this works:
INSERT INTO tbl_tours (TimeFrom)
output inserted.tourId, inserted.TimeFrom into tbl_tourData(tour_fk, timeFrom)
SELECT TimeFrom FROM #tmpTable;
Here is the revised db<>fiddle.
If you want additional information, you need to join back to another source.
When you do an insert ... output, the "output" part can only output whatever was inserted by the "insert" part. You can't reference data from the "inserting" table.
You do insert into tbl_tours(TimeFrom). So you're only inserting a single column - the TimeFrom column, and the tour_id column will be automatically inserted, so that's available too. But then you try to use 4 columns in the output list. Where would these extra two columns come from?
One way to do this in a single step is to use the merge statement, which can get data from the "inserting" source, not just the "inserted" table. Since you know you always want to do an insert, you can join on 1 = 0:
merge tbl_tours
using #tmpTable tmp on 1 = 0
when not matched then
insert (TimeFrom)
values (tmp.TimeFrom)
output inserted.tourId,
tmp.dispatchingId,
inserted.timeFrom, -- or tmp.timeFrom, doesn't matter which
tmp.TimeTo
into tbl_tourData (tour_fk, dispatchId, timeFrom, timeTo);
I should add: This is only possible because you don't actually have a foreign key defined from tbl_tourData to tbl_Tours. You probably do intend to have one given your column name. An output clause can't output into a table with a foreign key (or a primary key with a foreign key to it), so this approach won't work at all if you ever decide to actually create that foreign key. You'll have to do it in two steps. Either per Gordon's answer (insert and join), or by creating a whole new temp table matching the schema of tbl_tourData, outputting everything into that using merge, and then dumping the second temp table into the real tbl_tourData.

Cannot create non existing column in SQL database

I'm executing the following query on my db:
alter table Client drop column "IsVersionValid"
GO
alter table Client add "IsVersionValid" bit NULL
with the following result:
Msg 4924, Level 16, State 1, Line 1
ALTER TABLE DROP COLUMN failed because column 'IsVersionValid' does not exist in table 'Client'.
Msg 2705, Level 16, State 6, Line 3
Column names in each table must be unique. Column name 'IsVersionValid' in table 'Client' is specified more than once.
How is this possible? I'm trying to add a column that is not existing in my table. I tried doing this manually via the table designer of ssms. But this gives te same result.
This worked for me like a dream (also with double quotes), drop column if exists, and then recreate it!
ALTER TABLE Client
DROP COLUMN IF EXISTS IsVersionValid
GO
ALTER TABLE Client ADD IsVersionValid bit NULL
I solved my issue by creating a new table with the SELECT INTO statement. I then dropped the original table and renamed the new table. I think something was corrupted in this specific database. Other customers did not have this problem.

Trigger and Identity causing a "row value(s) updated or deleted either do not make the row unique or they alter multple rows" error during update

I am using Visual C# 2010 Express to create a table in SQL Server 2008. The column definitions are as follows:
SEQ -- type int, primary key, don't allow nulls
STID -- type int, don't allow nulls
CCODE -- type int, don't allow nulls
SCORE -- type int, don't allow nulls
ADDTIME -- type datetime, allow nulls
Then I set SEQ's "Is Identity" flag to be YES, the seed as 10001, and the increment as 1.
Next, I add a trigger for the table like this:
ALTER TRIGGER TRIG_ENROLL
ON dbo.ENROLL
FOR INSERT, UPDATE
AS
BEGIN
UPDATE t
SET ADDTIME = GETDATE()
FROM [dbo].[ENROLL] t
JOIN inserted i ON i.SEQ = t.SEQ
END
Finally, I add data directly to the table using the "Show Table Data" interface. Inserting the records are ok, as the ADDTIME and SEQ are automatically filled after I type the data, right click the row and select Execute SQL. However, when I update the SCORE column of an existing record, I get the following error:
Row value(s) updated or deleted either do not make the row unique or they alter multiple rows(2 rows)
I suspect it's the SEQ and the trigger that cause the problem. When I remove the trigger, updating the record does not cause any error. But what is the error?
In fact, a stranger thing is that when I try to use another computer and do the same procedures (build the table, add the trigger, insert the records, and then update them) using the same software, the error does not occur. Is it some kind of settings issue that I have overlooked?

Database protected from edit?

If I see this at the bottom of SQL Server Management Studio, does this mean that I do not have permissions to edit a row?
Or the column is a computed column, or populated by the IDENTITY property, or ROWVERSION, or ...
Try and edit the data in this table, you will see that all three columns are read only:
USE tempdb;
GO
CREATE TABLE dbo.flub
(
i INT IDENTITY(1,1),
x AS (i + 1),
r ROWVERSION
);
INSERT dbo.flub DEFAULT VALUES;
If you share the definition of the table, we can at least tell you why if it's because of one of these reasons (we'd need more info to confirm if it's a column-level permissions issue).
Better suggestion: stop using "Open Table", "Edit Top 200 rows" or any other Excel-looking grid to modify data. Write proper DML using UPDATE statements - I promise you the error messages you get when you try to modify a value that you can't will be much more descriptive in almost every case. Examples from above:
UPDATE dbo.flub SET i = 5;
Msg 8102, Level 16, State 1, Line 1
Cannot update identity column 'i'.
UPDATE dbo.flub SET x = 5;
Msg 271, Level 16, State 1, Line 1
The column "x" cannot be modified because it is either a computed column or is the result of a UNION operator.
UPDATE dbo.flub SET r = 0x00;
Msg 272, Level 16, State 1, Line 1
Cannot update a timestamp column.
I talk about several bugs in these UIs here, and others have been reported since. This one, for example, was just fixed in the 2012 version...
If that column you are currently trying to edit is an IDENTITY column, it will display this message.
If you're only getting that message on one or more columns in the table, it sounds like that cell may be a computed column, which by definition does not support updates. Or, it may be an identity column which also does not support updates.
To see if it's a computed column: If you go to the table designer, you will notice no data type specified and instead (in the properties below) a Computed Column Specification. Or, in the DDL you will see an AS clause for the column.

SQL: I need to take two fields I get as a result of a SELECT COUNT statement and populate a temp table with them

So I have a table which has a bunch of information and a bunch of records. But there will be one field in particular I care about, in this case #BegAttField# where only a subset of records have it populated. Many of them have the same value as one another as well.
What I need to do is get a count (minus 1) of all duplicates, then populate the first record in the bunch with that count value in a new field. I have another field I call BegProd that will match #BegAttField# for each "first" record.
I'm just stuck as to how to make this happen. I may have been on the right path, but who knows. The SELECT statement gets me two fields and as many records as their are unique #BegAttField#'s. But once I have them, I haven't been able to work with them.
Here's my whole set of code, trying to use a temporary table and SELECT INTO to try and populate it. (Note: the fields with # around the names are variables for this 3rd party app)
CREATE TABLE #temp (AttCount int, BegProd varchar(255))
SELECT COUNT(d.[#BegAttField#])-1 AS AttCount, d.[#BegAttField#] AS BegProd
INTO [#temp] FROM [Document] d
WHERE d.[#BegAttField#] IS NOT NULL GROUP BY [#BegAttField#]
UPDATE [Document] d SET d.[#NumAttach#] =
SELECT t.[AttCount] FROM [#temp] t INNER JOIN [Document] d1
WHERE t.[BegProd] = d1.[#BegAttField#]
DROP TABLE #temp
Unfortunately I'm running this script through a 3rd party database application that uses SQL as its back-end. So the errors I get are simply: "There is already an object named '#temp' in the database. Incorrect syntax near the keyword 'WHERE'. "
Comment out the CREATE TABLE statement. The SELECT INTO creates that #temp table.