insert data form one table to other in SQL - sql-server

I need to do the following to calculate customer reviews and show them on the page:
STEP 1.
Select data from T1 , count records and calculate the rating
SELECT COUNT(Rating) As ReviewCount,
ObjectID as ObID,
SUM(Rating)/COUNT(Rating) As ReviewScore
FROM [dbo].[Comment]
WHERE ObjectTypeID ='2' AND StatusID ='2'
GROUP BY ObjectID
This code works fine.
Next I want t do is insert calculated ReviewScore value into T2
INSERT INTO [dbo].[AttributeValue] (AttributeID, Value, SortOrder)
VALUES (5,ReviewScore, 29)
STEP 2
Next I want to do is get Identity (Last inserted ID) per each item for example:
DECLARE #ID INT= ##IDENTITY
STEP3
I want to get the ##Identity per inserted row in STEP 2 and Insert new data:
------- AtribbuteID is always 5
---- ObID is taken from the STEP 1
INSERT INTO [dbo].[AttributeObjectValue]
([AttributeID],[ObjectID],[Value],[AttributeValueID])
VALUES (5,ObID,ReviewScore,#ID)
I am trying to use the CTE table but cant get it to work:
BEGIN TRAN
GO
;WITH CTE (ReviewCount, ObjectID, ReviewScore)
AS
(
SELECT COUNT(Rating) As ReviewCount,
ObjectID as ObID,
SUM(Rating)/COUNT(Rating) As ReviewScore
FROM [dbo].[Comment]
WHERE ObjectTypeID ='2' AND StatusID ='2'
GROUP BY ObjectID
)
INSERT INTO [dbo].[AttributeValue] (AttributeID, Value, SortOrder)
VALUES (5,ReviewScore, 29)
SELECT ReviewCount FROM CTE
DECLARE #ID INT= ##IDENTITY
INSERT INTO [dbo].[AttributeObjectValue]
([AttributeID],[ObjectID],[Value],[AttributeValueID])
VALUES (5,ObID,ReviewCount,#ID)
GO
ROLLBACK TRAN
I will appreciate any help

Related

Stored procedure overdoes - inserting multiple tables

I expect below procedure to insert one row into the orders table, grab the PK then insert it into the orderlines table this PK with as many lines as many are in the datagridview on the form, then update the stock table with the quantity and finally insert 1 row into the payments table. Unfortunately it inserts as many rows to all 3 tables as many rows are in the datagridview. Is there a way to achieve my goal preferably without braking it down into more procedures?
CREATE PROCEDURE spNewSale
#customer INT, -- passed from a comboBox
#staff VARCHAR(12), -- the user logged in
#product INT, -- passed from a comboBox
#quantity INT -- passed from a textBox
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO orders (order_cust_id, order_staff_id, order_date) -- order_id (PK) = identity autoincremented
VALUES (#customer,
(SELECT logons.logon_st_id FROM logons
WHERE logons.logon_name = #staff), CURRENT_TIMESTAMP);
DECLARE #ordernbr INT = SCOPE_IDENTITY();
INSERT INTO orderlines (ol_order_id, ol_pro_id, ol_qty, ol_value) -- ol_id (PK) = identity autoincremented
VALUES (#ordernbr, #product, #quantity,
#quantity * (SELECT products.pro_price
FROM products WHERE pro_id = #product));
UPDATE products
SET pro_stock = (pro_stock - #quantity)
WHERE pro_id = #product;
INSERT INTO payments (paym_order_id, paym_amount, paym_taken_by, paym_date) -- paym_id (PK) = identity autoincremented
VALUES (#ordernbr,
(SELECT SUM(orderlines.ol_value) AS paym_amount
FROM orderlines WHERE orderlines.ol_order_id = #ordernbr),
(SELECT logons.logon_st_id FROM logons
WHERE logons.logon_name = #staff), CURRENT_TIMESTAMP)
END

SQL Server set based processing for orders and details

I have been trying to get acclimated to set based processing with SQL Server. Below is a simplified version of cursor processing for this task. It involves creating an order from items in a shopping cart. The order is created, line items are added to the order details table, the total is accumulated and eventually updated on the order table. Can anyone suggest how to do this with a set based approach instead of a cursor?
One other question is that in most cases the cursor will process at most 10 or 12 line items at a time. Is that enough of a reason to not have to consider the set based approach?
declare getCart2 cursor for
select MemberID,ProductID,Quantity,Price
from Carts
where MemberID = #MemberID
open getCart2
fetch next from getCart2 into #MemberID,#ProductID,#Quantity,#Price
Insert into Orders
(MemberID,TotalAmount0
Values
(#MemberID, 0.00)
set #OrderID = ##Identity
while ##FETCH_STATUS = 0 Begin
Insert into OrderDetails
(OrderID,ProductID,Quantity)
Values
(#OderID,#ProductID,#Quantity)
set #TotalAmout = #TotalAmount + (#Quantity * #Price)
set #PrevMemberID = #MemberID
fetch next from getCart2 into #MemberID,#ProductID,#Quantity,#Price
End
close getCart2
deallocate getCart2
Update Orders
Set TotalAmount = #TotalAmount
Where OrderID = #OrderID
Thanks for your help.
Here goes an approach:
In this case I am creating a temporary table variable that will store the order id's on it.
Then, it performs the insertions on the Order table and after that, in the OrderDetails.
Finally, it computes the TotalAmount and updates on the Orders table.
Although you don't have it in your code (and in mine as well), but I recommend you to use this code inside a transaction.
Hope it helps you improve your performance.
USE [tempdb];
GO
SET NOCOUNT ON;
IF OBJECT_ID(N'dbo.Carts', N'U') IS NOT NULL DROP TABLE [dbo].[Carts];
IF OBJECT_ID(N'dbo.Orders', N'U') IS NOT NULL DROP TABLE [dbo].[Orders];
IF OBJECT_ID(N'dbo.OrderDetails', N'U') IS NOT NULL DROP TABLE [dbo].[OrderDetails];
GO
-- Creates the tables like you have
CREATE TABLE [dbo].[Carts] (MemberID INT, ProductID INT, Quantity INT, Price DECIMAL(10, 2));
CREATE TABLE [dbo].[Orders] (OrderID INT IDENTITY(1, 1), MemberID INT, TotalAmount DECIMAL(10, 2));
CREATE TABLE [dbo].[OrderDetails] (OrderID INT, ProductID INT, Quantity INT);
-- Inserts dummy data
INSERT INTO [dbo].[Carts] VALUES (1001, 80, 5, 25.00);
INSERT INTO [dbo].[Carts] VALUES (1002, 120, 2, 12.90);
INSERT INTO [dbo].[Carts] VALUES (1010, 70, 3, 12.00)
INSERT INTO [dbo].[Carts] VALUES (1034, 176, 5, 45.00);
-- Temporary table that stores the inserted Order ID's
DECLARE #OrdersToProcess TABLE (OrderID INT, MemberID INT);
-- Inserts all Orders
INSERT INTO Orders (MemberID, TotalAmount)
OUTPUT inserted.OrderID, inserted.MemberID INTO #OrdersToProcess
SELECT MemberID, 0
FROM [dbo].[Carts]
-- Inserts order details
INSERT INTO OrderDetails (OrderID, ProductID, Quantity)
SELECT OrderID, ProductID, Quantity
FROM [dbo].[Carts] C
INNER JOIN #OrdersToProcess O ON C.MemberID = O.MemberID;
-- Updates order totals
UPDATE [dbo].[Orders]
SET TotalAmount = T.Total FROM
(
SELECT OrderID, SUM(Quantity * Price) AS [Total]
FROM [dbo].[Carts] C
INNER JOIN #OrdersToProcess O ON C.MemberID = O.MemberID
GROUP BY OrderID
) T
WHERE [dbo].[Orders].OrderID = T.OrderID
SELECT * FROM [dbo].[Orders];
SELECT * FROM [dbo].[OrderDetails];
Results:
As I understand your problem, this store procedure should be called when a particular member presses the check-out button, so it should create a single order with all the items in the cart of that member.
You can use something like this:
INSERT INTO Orders (MemberID, TotalAmount)
VALUES (#MemberID, 0)
SET #OrderID=SCOPE_IDENTITY()
INSERT INTO OrderDetails (OrderID, ProductID, Quantity)
SELECT OrderID, ProductID, Quantity
FROM [dbo].[Carts] C
WHERE C.MemberID=#MemberID
UPDATE dbo.Orders SET TotalAmount=(
SELECT SUM(c.Quantity*c.Price)
FROM dbo.Carts c
WHERE c.MemberID=#MemberID
) WHERE OrderID=#OrderID
It's true that this reads the Carts table twice, but with a proper index (on the MemberID column) that should be fast enough.

How to make a Trigger on Master-Detail

I Have the following scenario:
CREATE TABLE dbo.Orders
(
OrderID int IDENTITY (1,1) NOT NULL
, OrderVersion int DEFAULT(1)
, Customer varchar(30)
, ScheduleDate date
, PaymentOption int
);
CREATE TABLE dbo.OrdersItems
(
OrderItemsID int IDENTITY (1,1) NOT NULL
, OrderID int
, Product varchar(100)
, Qty int
, value decimal(18,2)
);
CREATE TABLE dbo.logOrders
(
OrderID int NOT NULL
, OrderVersion int DEFAULT(1)
, Customer varchar(30)
, ScheduleDate date
, PaymentOption int
);
CREATE TABLE dbo.logOrdersItems
(
OrderItemsID int NOT NULL
, OrderID int
, Product varchar(100)
, Qty int
, value decimal(18,2)
);
-- Insert values into the table.
INSERT INTO dbo.Orders (Customer , ScheduleDate, PaymentOption)
VALUES ('John', 2016-09-01, 1);
INSERT INTO dbo.OrdersItems( OrderId, Product, Qty, Value)
VALUES (1, 'Foo', 20, 35.658),
(1, 'Bla', 50, 100)
(1, 'XYZ', 10, 3589)
First Statement
UPDATE Orders set ScheduleDate = 2016-10-05 WHERE OrderId = 1
Second Statement
Delete From OrdersItems WHERE OrderItemsID = 2
UPDATE OrdersItems set Qty = 5 WHERE OrderItemsID = 1
Third Statement
Update Orders set PaymentOption = 2 WHERE OrderId = 1
Update OrdersItems set Value = 1050 WHERE OrderItemsID = 3
I am trying to figure out how to make a trigger that after each one of the Statements Sample above Insert on the log Tables the data before the changing. And setting the OrderVersion to OrderVersion + 1 on table Orders.
So on the log Tables I will have all versions after the later one.
Is it possible to make a single trigger to monitor both tables and execute getting the original data before the UPDATE, DELETE , INSERT statement to get the original data and INSERT on the logTables ?
Here comes a sample to explain better what result I want.
This is the Initial Data on table Orders and OrdersItems
If I make an Update on Orders ( any column ) or Make an Update,Insert,Delete on OrdersItems I need to Insert on respectively logTables the data on the image.
And with this I'll have on logOrders and logItems the original data and on the Orders and Items the altered data.
I Hope I could explain better what I mean.
You will need two triggers. The trigger for the Orders table handles Orders table update/delete. The trigger for the OrdersItems table does the same for OrdersItems. The triggers look like this:
For the Orders table:
CREATE TRIGGER dbo.Orders_trigger
ON dbo.Orders
AFTER DELETE,UPDATE
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO dbo.logOrders
SELECT * FROM DELETED;
INSERT INTO dbo.logOrdersItems
SELECT oi.* FROM OrdersItems oi
WHERE oi.OrderID IN (SELECT OrderId FROM DELETED);
END
GO
For OrdersItems:
CREATE TRIGGER dbo.OrdersItems_trigger
ON dbo.OrdersItems
AFTER DELETE,UPDATE
AS
BEGIN
SET NOCOUNT ON;
--Inerst the changed/deleted OrdersItems into the log
INSERT INTO dbo.logOrdersItems
SELECT * FROM DELETED
--Inserts the unchanged sibling OrdersItems records into the log
INSERT INTO dbo.logOrdersItems
SELECT oi.* FROM OrdersItems oi
WHERE oi.OrderId IN (SELECT DISTINCT OrderId FROM DELETED)
AND oi.OrderItemsID NOT IN (SELECT DISTINCT OrderItemsID FROM DELETED);
INSERT INTO dbo.logOrders
SELECT o.* FROM Orders o
WHERE o.OrderID IN (SELECT DISTINCT OrderId FROM DELETED);
END
GO
The Orders Trigger is fairly straightforward. Use the virtual DELETED table to insert the original version of the records into the log. Then join to the child OrdersItems records and insert them into the log as well. The way this is written, it will work even if you update or delete multiple Order records at a time.
The OrdersItems Trigger is a bit more complicated. You need to log the pre-chage version of the OrdersItems and Orders Records. But you also want (I think) to log the unchanged "sibling" OrdersItems records as well so that you have a complete picture of the records.
I know this is just your sample data, but you will want to add some kind of a timestamp to the records in the log tables. Otherwise you just end up with a bunch of duplicate rows and you cannot tell which is which. At the beginning of the trigger you can create a variable to hold the update datetime and then append that to your INSERT statement for the logs.
DECLARE #UpdateDateTime DATETIME;
SET #UpdateDateTime = GETUTCDATE();

T-SQL: Two Level Aggregation in Same Query

I have a query that joins a master and a detail table. Master table records are duplicated in results as expected. I get aggregation on detail table an it works fine. But I also need another aggregation on master table at the same time. But as master table is duplicated, aggregation results are duplicated too.
I want to demonstrate this situation as below;
If Object_Id('tempdb..#data') Is Not Null Drop Table #data
Create Table #data (Id int, GroupId int, Value int)
If Object_Id('tempdb..#groups') Is Not Null Drop Table #groups
Create Table #groups (Id int, Value int)
/* insert groups */
Insert #groups (Id, Value)
Values (1,100), (2,200), (3, 200)
/* insert data */
Insert #data (Id, GroupId, Value)
Values (1,1,10),
(2,1,20),
(3,2,50),
(4,2,60),
(5,2,70),
(6,3,90)
My select query is
Select Sum(data.Value) As Data_Value,
Sum(groups.Value) As Group_Value
From #data data
Inner Join #groups groups On groups.Id = data.GroupId
The result is;
Data_Value Group_Value
300 1000
Expected result is;
Data_Value Group_Value
300 500
Please note that, derived table or sub-query is not an option. Also Sum(Distinct groups.Value) is not suitable for my case.
If I am not wrong, you just want to sum value column of both table and show it in a single row. in that case you don't need to join those just select the sum as a column like :
SELECT (SELECT SUM(VALUE) AS Data_Value FROM #DATA),
(SELECT SUM(VALUE) AS Group_Value FROM #groups)
SELECT
(
Select Sum(d.Value) From #data d
WHERE EXISTS (SELECT 1 FROM #groups WHERE Id = d.GroupId )
) AS Data_Value
,(
SELECT Sum( g.Value) FROM #groups g
WHERE EXISTS (SELECT 1 FROM #data WHERE GroupId = g.Id)
) AS Group_Value
I'm not sure what you are looking for. But it seems like you want the value from one group and the collected value that represents a group in the data table.
In that case I would suggest something like this.
select Sum(t.Data_Value) as Data_Value, Sum(t.Group_Value) as Group_Value
from
(select Sum(data.Value) As Data_Value, groups.Value As Group_Value
from data
inner join groups on groups.Id = data.GroupId
group by groups.Id, groups.Value)
as t
The edit should do the trick for you.

sqlserver assigning last inserted Id to another column in one query

when this query is executed
DECLARE #Temp TABLE (ID INT IDENTITY,Name VARCHAR(50),ID2 INT NULL)
INSERT INTO #Temp ([Name]) VALUES ('Ali')
UPDATE #Temp SET ID2= (SELECT SCOPE_IDENTITY()) WHERE [ID]=(SELECT SCOPE_IDENTITY())
INSERT INTO #Temp ([Name]) VALUES ('Veli')
UPDATE #Temp SET ID2= (SELECT SCOPE_IDENTITY()) WHERE [ID]=(SELECT SCOPE_IDENTITY())
SELECT * FROM #Temp
We can get this table
ID-NAME-ID2
1 - Ali - 1
2 - Veli - 2
is there a way to do this in one insert query ( Assigning inserted id to another column without using idendity property in that column) ?
thanks a lot.
You want to make it a computed column like below
create TABLE Temp(ID INT IDENTITY,Name
VARCHAR(50),
ID2 AS ID PERSISTED);
Then insert rows ... your ID values will be persisted in ID2 column
INSERT INTO Temp ([Name])
VALUES ('Ali');
INSERT INTO Temp ([Name])
VALUES ('Veli');
INSERT INTO Temp ([Name])
VALUES ('Neli');
Which will results in
See a demo fiddle here http://sqlfiddle.com/#!3/59fca/1
EDIT:
If you can't change your table structure then the only way insert ID value to ID2 column is the way you are currently doing it cause in same insert statement the identity value is still not available and so it will be null.

Resources