Stored procedure insert into function - sql

I have two tables
First table is CUSTOMERS with columns CustomerId, CustomerName
Second table is LICENSES with columns LicenseId, Customer
The column Customer in the second table is the CustomerId from the First table
I wanted to create a stored procedure that insert values into table 2
Insert into Licenses (Customer)
Values(CustomerId)
How can I get this data from the other table?
Thanks in advance for any help

this looks to me like simply a syntax question - I think what you want is
INSERT INTO Licenses (Customer) SELECT CustomerId FROM customers where ...

CREATE PROCEDURE uspInsertToTable
#CustomerID INT OUTPUT,
#CustomerName VARCHAR(50),
#LicenseID INT,
AS
BEGIN
BEGIN TRY
BEGIN TRANSACTION;
INSERT INTO CUSTOMERS
VALUES (#CustomerName);
SET #CustomerID=SCOPE_IDENTITY();
INSERT INTO LICENSES
VALUES (#LicenseID, #CustomerID)
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0
BEGIN
ROLLBACK TRANSACTION;
END
END CATCH;
END;
];
if CustomerID is identity, I wish that it will be work... :)

If you're using SQL Server and your PK is autocorrelative using identity(1,1) do this:
CREATE PROC Register #CustomerName varchar(25)
AS BEGIN
INSERT INTO CUSTOMERS (Customer) VALUES (#CustomerName)
INSERT INTO LICENSES (SELECT MAX(CustomerId) FROM CUSTOMERS)
END
But if you don't want to use identity (1,1) do this:
CREATE PROC Register #CustomerName varchar(25)
AS BEGIN
DECLARE #idc int = SELECT MAX(CustomerId) FROM CUSTOMERS;
DECLARE #idl int = SELECT MAX(LicenseId) FROM LICENSES ;
INSERT INTO CUSTOMERS (CustomerId, CustomerName) VALUES (#idc,#CustomerName)
INSERT INTO LICENSES (LicenseId, Customer) VALUES (#idl,#idc)--I guess your field Customer is the foreign key
END

Related

SQL Update Trigger that inserts old values into audit table and only fires if two specific columns are being updated, otherwise returns error

I need to record all previous addresses and postcodes when they are updated in the Customer table.
Business requirement: Addresses cannot be changed without also updating the postcode and vice versa.
A mechanism to prevent this is required along with appropriate error messages.
I already created the table:
create table tblCustomerAudit
(
CustomerID int identity(1,1) not null,
CustomerName nvarchar(255) null,
CustomerAddress nvarchar(255) null,
CustomerPostcode nvarchar(255) null,
CardNumber nvarchar(255) null,
)
go
alter table tblCustomerAudit
add constraint FK_CustomerAudit
foreign key(CustomerID)
references CstmrEng.tblCustomer(CustomerID)
What would trigger look like? please help!
Perhaps you can do this with a stored procedure? Just be aware that stored procedures are not magic pixie dust, and maintaining them can be a nightmare.
You could have your trigger call a stored procedure that handles the particular constraint between CustomerAddress and CustomerPostcode.
This code is untested and may very well not work.
CREATE PROCEDURE UpdateCustomerAddressAndPostcode #CustomerID INT, #CustomerAddress NVARCHAR(255), #CustomerPostcode NVARCHAR(255)
AS BEGIN
IF (#CustomerAddress IS NULL OR #CustomerAddress = '')
BEGIN
PRINT 'Customer address must be present to modify Customer table.';
THROW;
END
IF (#CustomerPostcode IS NULL OR #CustomerPostcode = '')
BEGIN
PRINT 'Customer postcode must be present to modify Customer table.';
THROW;
END
INSERT INTO tblCustomerAudit (CustomerID, CustomerName, CustomerAddress, CustomerPostcode, CardNumber)
SELECT CustomerID, CustomerName, CustomerAddress, CustomerPostcode, CardNumber FROM Customer where CustomerID = #CustomerID;
-- just printing the table for example
SELECT * FROM tblCustomerAudit
-- make the change to Customer here or in the trigger
END
GO
CREATE TRIGGER CustomerTrigger
ON [dbo].[Customer]
INSTEAD OF INSERT
AS
DECLARE #CustomerID int
DECLARE #CustomerAddress nvarchar(255)
DECLARE #CustomerPostcode nvarchar(255)
BEGIN
SET NOCOUNT ON;
-- verify the data
EXEC UpdateCustomerAddressAndPostcode #CustomerID, #CustomerAddress, #CustomerPostcode
-- do the Customer change here or in the stored procedure
END
I really recommend a store procedure to control the update, insert, and delete, so you'll use the store procedure to pass the values before it goes to the table. If you use a trigger, then the values will be actually changed, then the trigger will be fired, and from the trigger you'll have to re-update the table with the old values if your conditions met. So, this is a kind of redundancy for me, which is why I recommended a store procedure to handle everything before change the table values.
anyhow, you can still use triggers with the advantage of deleted and inserted tables :
CREATE TRIGGER CustomerUpdate ON tblCustomerAudit
FOR UPDATE
AS
BEGIN
SET NOCOUNT ON;
DECLARE
#CustomerID INT
, #New_CustomerAddress nvarchar(255)
, #New_CustomerPostcode nvarchar(255)
, #Old_CustomerAddress nvarchar(255)
, #Old_CustomerPostcode nvarchar(255)
SELECT
#CustomerID = CustomerID
, #Old_CustomerAddress = CustomerAddress
, #Old_CustomerPostcode = CustomerPostcode
FROM
deleted
SELECT
#New_CustomerAddress = CustomerAddress
, #New_CustomerPostcode = CustomerPostcode
FROM
tblCustomerAudit
WHERE
CustomerID = #CustomerID
IF #Old_CustomerAddress = #New_CustomerAddress OR #New_CustomerPostcode = #Old_CustomerPostcode
BEGIN
-- IF one of them matches return the old values
UPDATE tblCustomerAudit
SET
CustomerAddress = #Old_CustomerAddress
, CustomerPostcode = #Old_CustomerPostcode
WHERE
CustomerID = #CustomerID
-- display an error message
RAISERROR( 'You need to change both address and postcode to save the new values', 18 , 0);
END
END
Something like this:
CREATE TRIGGER foo.bar ON foo.mytable
AFTER UPDATE
AS
IF (##ROWCOUNT_BIG = 0)
RETURN;
IF EXISTS (SELECT *
FROM foo.mytable AS t
JOIN inserted AS i
ON t.mykey = i.mykey
WHERE i.addr <> t.addr AND i.post = t.post -- address changes but post code doesn't
OR i.post <> t.post AND i.addr = t.addr -- post code changes by address doesn't
)
BEGIN
RAISERROR ('invalid changes', 16, 1);
ROLLBACK TRANSACTION;
RETURN
END;
GO

Import data into SQL Server with differences in some column

I have an Excel file that I imported into a table (with customerID, Customer name, accountID, Address, and accountmanagerID) in my SQL Server database.
Now I want to split the data into different tables, for customers, accounts, address, and managers.
The issue is that some customers have more then 5 accounts with different managers others with 2 different account with the same manager.
If I use the stored procedure shown here, I can only store one customer with one account.
But what I want to do is to store one customer with all the different accounts related to him.
This is my stored procedure:
ALTER PROCEDURE [dbo].[sp_Import_Client]
AS
BEGIN
TRUNCATE TABLE Customers
TRUNCATE TABLE Addresses
TRUNCATE TABLE Accounts
TRUNCATE TABLE Managers
DECLARE NewClient CURSOR LOCAL FAST_FORWARD READ_ONLY
FOR
SELECT
Distinct(CustomerName), --CustomerName
AccountID , --AccountNo
Address,
ManagerName -- manager
FROM
OldDetails
DECLARE #CustomerName VARCHAR(255)
DECLARE #Contact VARCHAR(300)
DECLARE #AccountNo VARCHAR(20)
DECLARE #Address1 VARCHAR(300)
DECLARE #CustomerID INT
OPEN NewClient
FETCH FROM NewClient INTO #CustomerName, #Manager, #AccountNo, #Address1
WHILE (##FETCH_STATUS = 0)
BEGIN
--- 1 Insert Customer
INSERT INTO Customers (CustomerName)
VALUES (#CustomerName)
---Set the CustomerID to continue with stage 2
SET #CustomerID = SCOPE_IDENTITY()
--- 2 Insert CustomerAddresses
INSERT INTO Address (CustomerID, Address1)
VALUES (#CustomerID, #Address1)
---3 Insert Managers
INSERT INTO Managers (CustomerID, Name)
VALUES (#CustomerID, #Contact)
---4 Insert Account
INSERT INTO Accounts(CustomerID, AccountNo, Manager)
VALUES (#CustomerID, #AccountNo, #manager)
FETCH NEXT FROM NewClient INTO #CustomerName, #Manager, #AccountNo, #Address1
END
CLOSE NewClient
DEALLOCATE NewClient
END
I think you might need to change the step 1 in your procedure to check if the customer already exists, and if it does, use it instead of inserting a new row for it. You can use the code block below instead of your INSERT statement:
-- SET THE #CustomerID
SET #CustomerID = NULL
SELECT #CustomerID = ID
FROM Customers
WHERE CustomerName = #CustomerName
IF #CustomerID IS NULL
BEGIN
--- 1 Insert Customer
INSERT INTO Customers (CustomerName)
VALUES (#CustomerName)
---Set the CustomerID to continue with stage 2
SET #CustomerID = SCOPE_IDENTITY()
END
This way, you can save only 1 customer record, and multiple rows for managers / accounts if needed.
Hope it makes sense.
EDIT
By the way, a small observation, you dont need to specify FAST_FORWARD and READ_ONLY together because FAST_FORWARD implies READ_ONLY and FORWARD_ONLY, so FAST_FORWARD will do it.

i am trying to execute the before insert trigger , but i m getting the sql errors

what i want to achieve is i have a table called orders.
i want to perform the before insert trigger on my orders table.i want to capture the
username of person performing INSERT into table.
one table called info which contain the user.
this is my code
create table orders
(
order_id int,
quantity int,
cost int,
total_cost int,
created_date datetime,
created_by varchar(20)
)
create trigger beforeInsertdata
before insert
on orders
for each row
declare
v_username varchar2(10);
begin
-- Find username of person performing INSERT into table
SELECT user INTO v_username
FROM info;
-- Update create_date field to current system date
:new.create_date := sysdate;
-- Update created_by field to the username of the person performing the INSERT
:new.created_by := v_username;
END;
--user information--
create table info
(
userid int ,
user_name varchar(10)
)
insert into info values(1,'vivek')
select * from info
Basically, triggers are classified into two main types:-
1)After Triggers (For Triggers)
2)Instead Of Triggers
and the syntax for trigger is
CREATE TRIGGER trigger_name ON table_name
[FOR|AFTER|INSTEAD OF] [INSERT|UPDATE|DELETE]
AS
//your code goes here
GO
NOTE : FOR keyword used for INSERT |UPDATE Command where as AFTER USED FOR DELETE Command.
It's hard to tell what you're really trying to do. I've modified your code sample so that it will work on SQL2K5 and made some assumptions about how you're wanting to use the connected user account.
CREATE TABLE orders (
order_id int,
quantity int,
cost int,
total_cost int,
created_date datetime,
created_by varchar(20)
);
CREATE TABLE info (
userid int,
user_name varchar(10)
);
INSERT INTO info
VALUES (1, 'vivek');
SELECT *
FROM info;
CREATE TRIGGER orders_InsteadOfInsert ON orders
INSTEAD OF INSERT AS BEGIN
SET NOCOUNT ON;
-- varchar(10) is to match your table, but probably should be larger
DECLARE #CurrentUser VarChar(10);
SELECT #CurrentUser = SYSTEM_USER;
IF (#CurrentUser NOT IN (SELECT user_name FROM info)) BEGIN
-- consider using an identity column for the key instead of this
INSERT INTO info (userid, user_name)
SELECT
ISNULL((SELECT MAX(userid) FROM info), 0) + 1,
#CurrentUser;
END;
INSERT INTO orders (order_id, quantity, cost, total_cost, created_date, created_by)
SELECT
INS.order_id,
INS.quantity,
INS.cost,
INS.total_cost,
GETDATE(),
#CurrentUser
FROM INSERTED INS;
END;

How do I insert from a table variable to a table with an identity column, while updating the the identity on the table variable?

I'm writing a SQL script to generate test data for our database. I'm generating the data in table variables (so I can track it later) and then inserting it into the real tables. The problem is, I need to track which rows I've added to the parent table, so that I can generate its child data later on in the script. For example:
CREATE TABLE Customer (
CustomerId INT IDENTITY,
Name VARCHAR(50)
)
CREATE TABLE Order (
OrderId INT IDENTITY,
CustomerId INT,
Product VARCHAR(50)
)
So, in my script, I create equivalent table variables:
DECLARE #Customer TABLE (
CustomerId INT IDENTITY,
Name VARCHAR(50)
) -- populate customers
DECLARE #Order TABLE (
OrderId INT IDENTITY,
CustomerId INT,
Product VARCHAR(50)
) -- populate orders
And I generate and insert sample data into each table variable.
Now, when I go to insert customers from my table variable into the real table, the CustomerId column in the table variable will become meaningless, as the real table has its own identity seed for its CustomerId column.
Is there a way I can track the new identity of each row inserted into the real table, in my table variable, so I can use a proper CustomerId for the order records? Or, is there a better way I should be going about this?
(Note: I originally started with an application to generate the test data, but it ran too slow during insert as > 1,000,000 records need to be generated.)
WHy do you need identity values on the table variables? If you use just int, you can isnert the ids after the insert is done. Grab them using the output clause. YOu might need an input values and an output values table varaiable to get this just right like this:
DECLARE #CustomerInputs TABLE (Name VARCHAR(50) )
DECLARE #CustomerOutputs TABLE (CustomerId INT ,Name VARCHAR(50) )
INSERT INTO CUSTOMERS (name)
OUTPUT inserted.Customerid, inserted.Name INTO #CustomerOutputs
SELECT Name FROM #CustomerInputs
SELECT * from #CustomerOutputs
You can insert the data to the table with a cursor and use the built-in function SCOPE_IDENTITY() to get the last id which was inserted in the current scope (by your script).
See this MSDN article for more information on SCOPE_IDENTITY.
Here is one way of doing it. If you can use it depends on your situation. You should not do it in production environment when users use your db.
-- Get the next identity values for Customer and Order
declare #NextCustomerID int
declare #NextOrderID int
set #NextCustomerID = IDENT_CURRENT('Customer')+1
set #NextOrderID = IDENT_CURRENT('Order')+1
-- Create tmp tables
create table #Customer (CustomerID int identity, Name varchar(50))
create table #Order (OrderID int identity, CustomerID int, Product varchar(50))
-- Reseed the identity columns in temp tables
dbcc checkident(#Customer, reseed, #NextCustomerID)
dbcc checkident(#Order, reseed, #NextOrderID)
-- Populate #Customer
-- Populate #Order
-- Allow insert to identity column on Customer
set identity_insert Customer on
-- Add rows to Customer
insert into Customer(CustomerId, Name)
select CustomerID, Name
from #Customer
-- Restore identity functionality on Customer
set identity_insert Customer off
-- Add rows to Order
set identity_insert [Order] on
insert into [Order](OrderID, CustomerID, Product)
select OrderID, CustomerID, Product
from #Order
set identity_insert [Order] off
-- Drop temp tables
drop table #Customer
drop table #Order
-- Check result
select * from [Order]
select * from Customer
The way I'd do it its first obtain the MAX(CustomerId) from your Customer Table. Then I'd get rid of the IDENTITY column on your variable table and do my own CustomerId using ROW_NUMBER() and the MaxCustomerId. It should be something like this:
DECLARE #MaxCustomerId INT
SELECT #MaxCustomerId = ISNULL(MAX(CustomerId),0)
FROM Customer
DECLARE #Customer TABLE (
CustomerId INT,
Name VARCHAR(50)
)
INSERT INTO #Customer(CustomerId, Name)
SELECT #MaxCustomerId + ROW_NUMBER() OVER(ORDER BY SomeColumn), Name
FROM YourDataTable
Or insert the values on a temp table, so you can use the same ids to fill your Order table.

Insert data into 3 related tables

How to insert data into 3 related tables (SQL Server)
For example, I have tables Customer <-- Customer_Address --> Address
After I insert data into Customer and Address, how do I insert the IDs from Customer and Address in to Customer_Address? (join table)
Thanks!
Use SCOPE_IDENTITY, ##IDENTITY can return a value from any scope:
DECLARE #CustomerId INT
DECLARE #AddressId INT
BEGIN TRANSACTION
INSERT INTO CUSTOMER (blah, blah) values (blah, blah)
SET #CustomerId = SCOPE_IDENTITY
INSERT INTO ADDRESS (blah, blah) values (blah, blah)
SET #AddressId = SCOPE_IDENTITY
INSERT INTO CUSTOMERADDRESS (CustomerId,AddressId) values (#CustomerId,#AddressId)
COMMIT TRANSACTION
If you want to insert more than one row you can use the output clause:
declare #insertedAddresses table (OriginalID int, AddressID int);
declare #insertedCustomers table (OriginalID int, CustomerID int);
insert into dbo.Addresses (AddressData)
output source.OriginalID, inserted.AddressID into #insertedAddresses
select AddressData from source;
insert into dbo.Customers (CustomerData)
output source.OriginalID, inserted.CustomerID into #insertedCustomers
select CustomerData from source;
insert into dbo.Customer_Address (AddressID, CustomerID)
select a.AddressID, c.CustomerID
from #insertedAddresses a inner join #insertedCustomers c on c.OriginalID=a.OriginalID;
If the IDs from the Customer and Address tables are Identity columns, you can store the new ID into a variable.
DECLARE #CustomerID int
SELECT #CustomerID = ##IDENTITY FROM TABLE CUSTOMER
Similar syntax could be used for the Address table. Then in your INSERT statement you can do this:
INSERT INTO Customer_Address (CustomerID, AddressID)
VALUES (#CustomerID, #AddressID)
use a transaction, and remember the identities. in sql server -
declare #CustomerId int
declare #AddressId int
begin tran
insert into Customer (blah, blah) values (blah, blah)
set #CustomerId = ##IDENTITY --assuming there are no triggers
insert into [Address] (blah, blah) values (blah, blah)
set #AddressId = ##IDENTITY --once again, no triggers to mess up the ##IDENTITY
insert into CustomerAddress(CustomerId,AddressId) values (#CustomerId,#AddressId)
commit
Here is one way.
Declare CustomerID int, AddressID int
insert into Customer (list, of, fields) values (list, of, values)
select #CustomerID=scope_Identity()
insert into Address (list, of, fields) values (list, of, values)
select #AddressID =scope_Identity()
insert into Customer_Address (CustomerID, AddressID) values (#CustomerID, #AddressID)
If it is for an application, why don't just use Linq, you can accomplish things like this in just a few minutes, in a more compressive way and more thigh to the business logic model.
If not the I highly recommend the transaction and SCOPE_IDENTITY version, because if you are in a heavy loaded system, an other operation can do an insert and change the identity value so you will end with inconsistent data.