Inserting into Output table in an insert with from clausule [duplicate] - sql

This question already has answers here:
Using OUTPUT clause to insert value not in INSERTED
(2 answers)
Closed 4 years ago.
I'm trying to duplicate some rows in a table, with a couple of changed values, and I also need to store an old (that will be lost) id to do further processing later. I'm trying to use the Output Clausule to store that information, but SQL Server is throwing the following error:
Msg 4104 <...> The multi-part identifier could not be bound.
This is the table I'm duplicating the data (slightly modified to reduce the number of columns):
Create Table Elements
(
id int Identity(0,1) not null, --PK
name varchar(50) not null,
modelID int not null, --FK
constraint PK_Elements primary key (id)
);
And this is my my query:
declare #outputTable table
(
oldElementID int,
id int,
name varchar(50),
modelID bigint
);
Insert into Elements
(name, modelID)
Output e.id as oldElementID,
Inserted.id,
Inserted.name,
Inserted.modelID into #outputTable
select e.name, #newModelID
from Elements as e
where e.modelID = #oldModelID
Note: #oldModelID and #newModelID are previously declared and set.
I'm not sure if my logic is wrong and I have to take a different approach (but I was sure it was possible to do it this way). Or if I simply have an error that I can't quite put my finger on it.
Any help would be appreciated.
Thanks!

I recreated the problem like this:
CREATE TABLE #a (a INT, b INT)
INSERT INTO #a (a,b) VALUES (42, 43)
INSERT INTO #a (a, b)
OUTPUT a.a, a.b, inserted.a, inserted.b
SELECT a.b, a.a
FROM #a a
The insert operation produces the messages:
Msg 4104, Level 16, State 1, Line 7
The multi-part identifier "a.a" could not be bound.
Msg 4104, Level 16, State 1, Line 7
The multi-part identifier "a.b" could not be bound.
that's because the INSERT command cannot see the alias 'a' that I used in the select command.

Related

SQL Creating a procedure, but it doesn't see my tables

I created a new database called sample1. In it I created 3 tables: "address, contact and buyer"
CREATE TABLE address
(
address_id int NOT NULL identity(1,1) primary key,
street_name varchar(30)
);
CREATE TABLE contact
(
contact_id int NOT NULL identity(1,1) primary key,
phone_num varchar(20) NOT NULL,
address_id int NOT NULL foreign key REFERENCES address(address_id)
);
CREATE TABLE buyer
(
username char(20) NOT NULL primary key,
name char(40) NOT NULL,
contact_id int NOT NULL foreign key REFERENCES contact(contact_id)
);
The tables were created successfully, now I'm trying to create a stored procedure as follows:
create procedure csc
(
#username char(20),
#address_id int,
#contact_id int
)
as
set nocount on;
insert into [dbo].[buyer] values (#username);
insert into [dbo].[address_id] values (#address_id);
insert into [dbo].[contact_id] values (#contact_id);
go
But I get the following errors:
Msg 137, Level 15, State 2, Line 4
Must declare the scalar variable "#username".
Msg 137, Level 15, State 2, Line 10
Must declare the scalar variable "#username".
Msg 137, Level 15, State 2, Line 11
Must declare the scalar variable "#address_id".
Msg 137, Level 15, State 2, Line 12
Must declare the scalar variable "#contact_id".
My questions / errors:
Are my tables logical? Should I be using buyer username as the PK for address / contact ID (my teacher suggested this, but I think it's bad practice)
When creating my procedure, I get red lines on [dbo].[buyer], [dbo].
[address_id], [dbo].[contact_id], can I ignore these?
How in the world do I fix the errors?
Thanks in advance.
1, are my tables logical? should I be using buyer username as the PK
for address / contact ID (my teacher suggested this, but I think it's
bad practice)
It depends whether a buyer is always defined by their username and that won't get updated. This is an age old debate between natural and surrogate keys, you can have a google and find some pretty convincing arguments for either side. If you think it's bad practice then you'll need to be able to explain why you think this. I would certainly avoid using the char datatype unless you really wanted to use it, it will blank pad your data up to it's specified length.
2, when creating my procedure, I get red lines on [dbo].[buyer],
[dbo]. [address_id], [dbo].[contact_id], can I ignore these?
That's because when SSMS last looked, those tables do not exist. If you press CTRL+SHIFT+R, you will refresh intelligence and it will check again. It'll still fail though because address_id nor contact_id are tables.
3, how in the world do I fix the errors?
You fix the table names. But once you've done that you'll reach another error as you aren't specifying what column your values should be inserted into and there are multiple it can use. The problem is that your code wants to insert your primary key columns but with no additional data - so really you need to complete your code so that all the required data is input and the columns they are inserting into are explicitly mentioned.
e.g.
insert into [dbo].[buyer] (username, name, contact_id) values (#username, #name, #contact_id);
But.. you are inserting the contact row at the same time and you've declared it as an identity column, should you really know the value of this column already or should you be inserting the other values into the table and generating a new contact_id with it?
Perhaps your procedure should really start
create procedure csc(
#username char(20),
#name char(40),
#street_name varchar(30),
#phone_num varchar(20)
)
And then build up your insert statements from that data. Something like:
as
set nocount on;
declare #address_id int;
declare #contact_id int;
insert into [dbo].[address] (street_name) values (#street_name);
set #address_id = SCOPE_IDENTITY() ;
insert into [dbo].contact (phone_num, address_id) values (#phone_num, #address_id);
set #contact_id = SCOPE_IDENTITY() ;
insert into [dbo].[buyer] (username, name, contact_id) values (#username, #name, #contact_id);
go
And to demo:
exec csc 'andy','andy','Nice Road',42
select * from buyer;
select * from contact;
select * from address;
username name contact_id
-------------------- ---------------------------------------- -----------
andy andy 3
(1 row affected)
contact_id phone_num address_id
----------- -------------------- -----------
3 42 3
(1 row affected)
address_id street_name
----------- ------------------------------
3 Nice Road
(1 row affected)
It looks like you've got a couple of typos. Try it like this
create procedure csc
#username char(20),
#address_id int,
#contact_id int
as
set nocount on;
insert into [dbo].[buyer] values (#username);
insert into [dbo].[address] values (#address_id);
insert into [dbo].[contact] values (#contact_id);
go
Use a begin/end block:
create procedure csc (
#username char(20),
#address_id int,
#contact_id int
) as
begin
set nocount on;
insert into [dbo].[buyer] values (#username);
insert into [dbo].[address_id] values (#address_id);
insert into [dbo].[contact_id] values (#contact_id);
end;
go

How do I select insert into select a table which already has values in the primary key column without adding new rows?

I'm working on a database for my school project in which I have to produce a functional database by normalizing sample tables given to us.
One table I'm having trouble with is itineraries. I produce 3 tables from the normalization which are "Destinations", "Itineraries" and "Itinerary_Destinations".
The code for Destinations is:
create table Destinations
(
DestinationID varchar(5) primary key,
Name varchar(45)
);
The code for Itineraries is:
create table Itineraries
(
ItineraryID varchar(5),
Name varchar(45)
);
The code for the last table is:
create table Itinerary_Destinations
(
DI varchar(5) primary key,
ItineraryID varchar(5) foreign key references Itineraries(ItineraryID),
Itinerary_Name varchar(45),
DestinationID varchar(5) foreign key references Destinations(DestinationID),
Destination_Name varchar(45)
);
Data has already been inserted into all 3 tables with the exception of 'Destination_Name' and 'Itinerary_Name' columns. The code I'm attempting to use is returning as error. The code is shown below.
insert into Itinerary_Destinations (Itinerary_name)
select Name from Itineraries where
Itineraries.ItineraryID = ItineraryID;
The error it returns is
Msg 515, Level 16, State 2, Line 1 Cannot insert the value NULL into
column 'DI', table 'DDDAssignment.dbo.Itinerary_Destinations'; column
does not allow nulls. INSERT fails. The statement has been terminated.
Is there a method to accomplish the task of inserting the Destination_Name and Itinerary_Name without creating new records that require primary keys?
Or should I do it manually?
If you want to modify records which already exist, then you should be using an UPDATE rather than an INSERT:
UPDATE a
SET Itinerary_name = b.Name
FROM Itinerary_Destinations a
INNER JOIN Itinerary_name b
ON a.ItineraryID = b.ItineraryID;
But, if you do have some data which is not already logically associated with the Itinerary_Destinations table, then using an insert is appropriate.
use coalesce funtion in case null it will insert blank string, as your column does not allow null value thats why you got that error in your query
insert into Itinerary_Destinations (Itinerary_name)
select coalesce(Name,' ') from Itineraries where
Itineraries.ItineraryID = ItineraryID;

How to add row to a SQL Server table with uniqueidentifier type column?

I need to add a row to a SQL Server table called Customers.
Here is the table design:
Here how I try to add a row to the table above:
INSERT INTO Customers (Id, Name)
VALUES (1, 'test');
But I get this error:
Msg 206, Level 16, State 2, Line 1
Operand type clash: int is incompatible with uniqueidentifier
As you can see the ID column of type uniqueidentifier. How do I add a uniqueidentifier to the column above?
The uniqueidentifier data type stores 16-byte binary values that operate as globally unique identifiers (GUIDs).
Example using:
insert into customers values('7E3E3BC1-9B15-473C-A45A-46D89689156C', 'Tomas')
insert into customers values(newid(), 'Tomas')
See more details: https://technet.microsoft.com/en-US/library/ms190215(v=sql.105).aspx
Use newid():
INSERT INTO Customers(Id, Name) VALUES (newid(), 1);
Note that this would often be done using a default value:
create table customers (
id uniqueidentifier default newid(),
. . .
);
For various technical reasons, it is not recommended to make the uniqueidentifier column a primary key -- although you can get around some of the problems using newsequentialid().. Instead, make it a unique key and have another key (typically an identity() column) as the primary key.

Can't find columns after declaration in SQL Server

I have a problem. I am creating a trigger which will prompt the user to not create a purchase order with a higher cost to their selling cost. I have declared a column on my script but its still showing this error:
Msg 207, Level 16, State 1, Procedure _trgZSCheckPrice, Line 31 [Batch Start Line 7]
Invalid column name 'fExclPrice'
The two columns are #Check and #Excl
I have attached my code below any help is advisable:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TRIGGER _trgZSCheckPrice
ON [dbo].[_btblInvoiceLines]
FOR INSERT, UPDATE
AS
BEGIN
SET NOCOUNT ON;
DECLARE
#Valid int,
#ValidPO int,
#DocType int,
#DocState int,
#Check int,
#Excl float,
#POPrice float
SELECT
#Check = ubIICheck,
#Excl = fExclPrice,
#POPrice = fUnitPriceExcl
FROM
INSERTED
SELECT
#Excl = fExclPrice,
#Check = ubIICheck
FROM
stkitem A
INNER JOIN
_etblPriceListPrices B ON A.StockLink = B.iStockID
SELECT
#POPrice = fUnitPriceExcl
FROM
_btblInvoiceLines C
LEFT JOIN
InvNum D ON C.iInvoiceID = D.AutoIndex
BEGIN
IF (#DocType = 5 AND #DocState <> 7) AND #CHECK = 1
BEGIN
IF #Excl > #POPrice
BEGIN
RAISERROR ('Message from Management:
You are not allowed to Purchase above Selling Cost.
The transaction will be rolled back. ', 16, 1)
ROLLBACK TRANSACTION
END
END
END
END
It seems like ideally, here, you'd be implementing this as a multi-table CHECK constraint (in fact, in standard SQL, these are called ASSERTIONs. So far as I'm aware, only Postgre implements them).
If you're happy to trade away the ability to completely control the error message, I'd usually prefer to implement this in a declarative manner rather than relying on a trigger.
Here's how to do such a check with an indexed view. First a couple of tables that are somewhat like your problem domain (but not very fleshed out since you didn't put any definitions in your question):
create table dbo.ListPrices (
ID int not null,
Price decimal(12,4) not null,
constraint PK_ListPrices PRIMARY KEY (ID)
)
create table dbo.Orders (
ID int not null,
ListPriceID int not null,
MyPrice decimal(12,4) not null,
constraint PK_Orders PRIMARY KEY (ID),
constraint FK_Orders_ListPrices FOREIGN KEY (ListPriceID)
references dbo.ListPrices (ID)
)
go
insert into dbo.ListPrices (ID,Price) values (1,12.50),(2,25.00)
And now we create a special helper table. It's not needed if you already have a suitable table (such as a numbers table) in your database:
create table dbo.Two (
n int not null,
constraint PK_Two PRIMARY KEY (n),
constraint CK_Two_Only CHECK (n in (1,2))
)
go
insert into dbo.Two (n) values (1),(2)
And now we create the view:
go
create view dbo.DRI_NoOrderPricesOverListPrices
with schemabinding
as
select
1 as p /* Constant */
from
dbo.Two t
cross join
dbo.Orders o
inner join
dbo.ListPrices lp
on
o.ListPriceID = lp.ID
where
/*Conditions for failure*/
o.MyPrice > lp.Price
go
create unique clustered index IX_NoOrderPricesOverListPrices
on DRI_NoOrderPricesOverListPrices(p)
The key feature here is that the view joins all of the tables we're interested in together and we can put whatever conditions we like in the where clause, referencing multiple tables, comparing column values, etc.
The conditions we're specifying is for what shouldn't be allowed in the database. So, we're saying we should be able to insert a row in Orders provided that its price is less than or equal to the price in line items.
And we can. This insert succeeds:
insert into Orders (ID,ListPriceID,MyPrice) values (1,2,17.00)
And this one fails:
insert into Orders (ID,ListPriceID,MyPrice) values (2,1,17.00)
Msg 2601, Level 14, State 1, Line 42
Cannot insert duplicate key row in object 'dbo.DRI_NoOrderPricesOverListPrices' with unique index 'IX_NoOrderPricesOverListPrices'. The duplicate key value is (1).
(As I said at the top, we don't get so much control over the error message - but making a good choice of name here should make it reasonable to deduce what's happening and directly exposing SQL Server error messages to the users is something I'd generally try to avoid)

How do I fix my trigger query so that the multi-part identifiers can be bound?

I'm working on a trigger which will allow me to store all the previous data inside specified columns on my Customer table into a specified audit-table once those columns are updated. The fields I'm taking from the customer table and storing are specific, as there are multiple fields on the customers table and I am only trying to store the (CustomerID, CustomerAddress and CustomerPostcode)
Here is the fields in the Customer table:
[CustomerID]
[CustomerName]
[CustomerAddress]
[CustomerPostcode]
[CustomerTelephone]
[CardNumber]
[CountyID]
I am only trying to take [CustomerID], [CustomerAddress] and [CustomerPostcode]
In order to store these specific fields I set up an audit table to the best of my ability which will store those fields, but also display auto-generated fields based on the trigger.
Here is the Audit-Table I set up:
CREATE TABLE Customer_Audit
(
Cust_UpdateID int IDENTITY (1,1),
Cust_User char (8),
Cust_Update_Date date,
CustomerID int,
CustomerAddresss nvarchar (255),
CustomerPostCode nvarchar (255),
CONSTRAINT [pk_Cust_UpdateID] PRIMARY KEY (Cust_UpdateID)
)
Here is the trigger query I've set up:
CREATE TRIGGER Customer_Update_Trigger ON tblCustomer
AFTER UPDATE
AS
BEGIN
INSERT INTO Customer_Audit (Cust_User, Cust_Update_Date, Cust_ID, CustomerAddresss, CustomerPostCode)
SELECT
CURRENT_USER,
GETDATE(),
d.CustomerID,
d.CustomerAddress,
d.CustomerPostcode
FROM deleted
END
In order to store the data I'm trying to take the fields from the deleted table but every time I do it I keep getting the following error messages:
Msg 4104, Level 16, State 1, Procedure Customer_Update_Trigger, Line 11
The multi-part identifier "d.CustomerID" could not be bound.
Msg 4104, Level 16, State 1, Procedure Customer_Update_Trigger, Line 12
The multi-part identifier "d.CustomerAddress" could not be bound.
Msg 4104, Level 16, State 1, Procedure Customer_Update_Trigger, Line 13
The multi-part identifier "d.CustomerPostcode" could not be bound
I don't know what it is I'm doing wrong, I know the field names in the deleted table match the field names in the customer table, but it still refused to process.
you are missing an alias for deleted, try adding FROM deleted as d
CREATE TRIGGER Customer_Update_Trigger ON tblCustomer
AFTER UPDATE AS
BEGIN;
INSERT INTO Customer_Audit (
Cust_User
, Cust_Update_Date
, Cust_ID
, CustomerAddresss
, CustomerPostCode
)
SELECT
CURRENT_USER
, GETDATE()
, d.CustomerID
, d.CustomerAddress
, d.CustomerPostcode
FROM deleted as d;
END;