I just can't get my head around this today - so your help is much appreciated.
Table Structure
Create Table #trans
(
TransactionId int,
AccountNumber varchar(10),
TransactionAmount money,
TransactionDate DateTime
)
Create Table #payments
(
PaymentId int,
AccountNumber varchar(10),
PaymentAmount money,
PaymentDate
)
Example Data
Insert Into #trans
Values ( 500500 ,'10000001', 10000.00, '2008-10-02')
GO
Insert Into #trans
Values ( 500501 ,'10000001', 10000.00, '2008-10-02')
GO
Insert Into #trans
Values ( 500502 ,'10000001', 10000.00, '2008-10-02')
GO
Insert Into #payments
Values ( 0001,'10000001', 10000.00, '2008-10-02')
GO
Insert Into #payments
Values ( 0002,'10000001', 10000.00, '2008-10-02')
GO
Insert Into #payments
Values ( 0003,'10000001', 10000.00, '2008-10-02')
GO
Expected Results
I need to be able to match the transactions with the payments. So basically I will get:
TransactionId PaymentId
500500 0001
500501 0002
500502 0003
The transaction being matched on the account number, date of payment and amount.
It seems really simple but I just cant seem to work it out.
Update
To try to clarify my situation, I have a list of historical transactions as per the above table. I have file containing payments, again historical. I need to match the transactions to the payments within the file.
Why?
to find any transactions that don't exist in the file.
to find any payments within the file that don't have a corresponding transaction
to create a "link" table that will contain the TransactionID and PaymentID so that in future anyone else querying this data won't have the same issue.
The reason you can't work it out is because there is nothing that relates a Transaction to a Payment.
You would need to add a foreign key to either one of the tables that references the related piece of information in the other table for the result to have any meaning.
I would modify the tables like:
Create Table #payments
(
PaymentId int,
AccountNumber varchar(10),
PaymentAmount money,
PaymentDate,
TransactionId int,
foreign key (TransactionId) references #trans(TransactionId)
)
Create Table #trans
(
TransactionId int,
AccountNumber varchar(10),
TransactionAmount money,
TransactionDate DateTime
)
And then you can do a simple query (or a join if you want more than just the ids):
select TransactionId, PaymentId from #payments
Why is payment a separate table? A payment is just one type of transaction. If there's extra data that goes with a payment, that's fine. But even in that case, you shouldn't duplicate the basic transaction info in the payment table. Put it in the transaction table and give the payment table a TransactionID column that you will use to relate it back to the transaction.
Related
I have 2 tables: Order and product_order. Every order has some product in it and that's because I store products another table.
Table Order:
Id name
Table PRODUCT_ORDER:
id product_id order_id
Before I start to insert, I don't know what the Order Id is. I want to insert the data into both tables at once and I need the order id to do that.
Both id's are auto incremented. I'm using SQL Server. I can insert first order and then find the id of the order and than execute the second insert, but I want to do these both to execute at once.
The output clause is your friend here.
DECLARE #Orders TABLE (OrderID INT IDENTITY, OrderDateUTC DATETIME, CustomerID INT)
DECLARE #OrderItems TABLE (OrderItemID INT IDENTITY, OrderID INT, ProductID INT, Quantity INT, Priority TINYINT)
We'll use these table variables as demo tables with IDs to insert into. You're liking going to be passing the set of items for an order in together, but for the purpose of a demo we'll ad hoc them as a VALUES list.
DECLARE #Output TABLE (OrderID INT)
INSERT INTO #Orders (OrderDateUTC, CustomerID)
OUTPUT INSERTED.OrderID INTO #Output
VALUES (GETUTCDATE(), 1)
We inserted the Order into the Orders table, and used the OUTPUT clause to cause the inserted (and generated by the engine) into the table variable #Output. We can now use this table however we'd like:
INSERT INTO #OrderItems (OrderID, ProductID, Quantity, Priority)
SELECT OrderID, ProductID, Quantity, Priority
FROM (VALUES (5,1,1),(2,1,2),(3,1,3)) AS x(ProductID, Quantity, Priority)
CROSS APPLY #Output
We cross applied it to our items list, and inseted it as if it was any other row.
DELETE FROM #Output
INSERT INTO #Orders (OrderDateUTC, CustomerID)
OUTPUT INSERTED.OrderID INTO #Output
VALUES (GETUTCDATE(), 1)
INSERT INTO #OrderItems (OrderID, ProductID, Quantity, Priority)
SELECT OrderID, ProductID, Quantity, Priority
FROM (VALUES (1,1,1)) AS x(ProductID, Quantity, Priority)
CROSS APPLY #Output
Just to demo a little farther here's another insert. (You likely wouldn't need the DELETE normally, but we're still using the same variable here)
Now when we select that data we can see the two separate orders, with their IDs and the products that belong to them:
SELECT *
FROM #Orders o
INNER JOIN #OrderItems oi
ON o.OrderID = oi.OrderID
OrderID OrderDateUTC CustomerID OrderItemID OrderID ProductID Quantity Priority
------------------------------------------------------------------------------------------------
1 2022-12-08 23:23:21.923 1 1 1 5 1 1
1 2022-12-08 23:23:21.923 1 2 1 2 1 2
1 2022-12-08 23:23:21.923 1 3 1 3 1 3
2 2022-12-08 23:23:21.927 1 4 2 1 1 1
Dale is correct. You cannot insert into multiple tables at once, but if you use a stored procedure to handle your inserts, you can capture the ID and use it in the next insert.
-- table definitions
create table [order]([id] int identity, [name] nvarchar(100))
go
create table [product_order]([id] int identity, [product_id] nvarchar(100), [order_id] int)
go
-- stored procedure to handle inserts
create procedure InsertProductWithOrder(
#OrderName nvarchar(100),
#ProductID nvarchar(100))
as
begin
declare #orderID int
insert into [order] ([name]) values(#OrderName)
select #orderID = ##identity
insert into [product_order]([product_id], [order_id]) values(#ProductID, #orderID)
end
go
-- insert records using the stored procedure
exec InsertProductWithOrder 'Order ONE', 'AAAAA'
exec InsertProductWithOrder 'Order TWO', 'BBBBB'
-- verify the results
select * from [order]
select * from [product_order]
I'm using SQL Server 2017 (v14.0).
I have two tables with one-to-many relationship. I need to group the rows in the "Orders" table and by this info create the row in the "Transactions" table, then I need set the relationship - for a created transaction I need set the TransactionId to related order's rows in one query/transaction flow to keep the consistency.
I correctly insert the new row by grouped data, but can't update the related rows in "Orders" table to set the relationship (TransactionId) for related "Transactions".
Can you, please, help with composing the query statement or get the clue to move in the right direction?
CREATE TABLE [dbo].[Orders]
(
[OrderId] INT NOT NULL,
[TransactionId] INT NULL,
[OrderVolume] DECIMAL(18, 8) NOT NULL,
[OrderCurrencyId] INT NOT NULL,
)
CREATE TABLE [dbo].[Transactions]
(
[TransactionId] INT NULL,
[Volume] DECIMAL(18, 8) NOT NULL,
)
INSERT INTO Transactions (Volume)
OUTPUT INSERTED.[TransactionId] --also need to update the rows in "Orders" that take a part in grouping to set the relationship
SELECT
SUM(OrderVolume) AS OrderVolume,
FROM Orders
GROUP BY Orders.OrderCurrencyId
The problem with the OUTPUT clause in an INSERT statement is that it doesn't allow you to select any field from the source table.
You can achieve this using MERGE statement instead:
DECLARE #t TABLE([TransactionId] INT, [OrderCurrencyId] INT)
MERGE Transactions trgt
USING
(
SELECT
SUM(OrderVolume) AS OrderVolume
, Orders.OrderCurrencyId AS OrderCurrencyId
FROM
Orders
GROUP BY
Orders.OrderCurrencyId
) AS src ON (1=0)
WHEN NOT MATCHED THEN
INSERT ( [Volume] ) VALUES (src.OrderVolume)
OUTPUT [inserted].[TransactionId], src.[OrderCurrencyId]
INTO #t;
UPDATE Orders
SET TransactionId = t.TransactionId
FROM Orders
JOIN #t t ON Orders.OrderCurrencyId = t.OrderCurrencyId
Demo here
I have 2 tables.
create table Sales
(CustomerKey int
,ProductKey int
,CustomersProductsKey int
,SalesAmount decimal(19,4))
Create Table CustomersProducts
(CustomersProductsKey int IDENTITY(1,1),
CustomerKey int,
ProductKey int,
Attribute1 int,
Attribute2 varchar(max))
Currently when I add data to the sales table, I need to insert any new customerkey productkey combinations into the CustomersProducts table and then update the sales table with the resulting CustomersProductsKey identity value. This works.
Is there anyway that I can do this in one step? I don't know if a Merge can do an insert and update on the same if not matched step.
I also could be just looking at this the wrong way as well.
Thanks,
EDIT:
As you can imagine, the fact that I need to use a surrogate key is part of the design. It's needed for a BO report. Otherwise there would really be no need for CustomersProductsKey at all.
If add only one step to make it work,
I think we need create a another table and create trigger on the new table and CustomersProducts
create table CustomersSalesProducts
(CustomerKey int
,ProductKey int
,SalesAmount decimal(19,4)
,Attribute1 int
,Attribute2 varchar(max))
create trigger test1 on CustomersSalesProducts After Insert
as
begin
insert Sales select CustomerKey , ProductKey , 0, SalesAmount from inserted
insert CustomersProducts select CustomerKey , ProductKey , Attribute1, Attribute2 from inserted
end
go
create trigger test2 on CustomersProducts after insert
as
begin
Update Sales set CustomersProductsKey = inserted.CustomersProductsKey
from inserted , Sales
where inserted.CustomerKey = Sales.CustomerKey
and inserted.ProductKey = Sales.ProductKey
end
go
Test Script:
insert CustomersSalesProducts select 3,3,300,3,'Attribute2'
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.
i have a table called table1 and it has following columns.
suppose there are records like localamount is 20,000, 30000,50000, 100000 then as per my condition i have to delete records from this table according to the group by set of site id, till id, transid,shift id where localamount exceeds 10,000... the rest of the records can be available?
my aim is to delete rows from this table where local amount is exceeds 10,0000 according to site id, till id, transid,shift id
SiteId varchar(10),
TillId tinyint,
ShiftId int,
TransId int,
TranDate datetime,
SettlementType varchar(5),
CreditCardNumber varchar(25),
ProductTypeCode varchar(10),
NewProductTypeCode varchar(10),
TransactionType int,
ForeignAmount money,
LocalAmount money,
ProductCode varchar(10)
Im not sure I understand what you are saying, but couldn't you do this without a group by?
delete from table1 where LocalAmount > 10,0000[sic] and SiteId = whatever and TillId = whatever...
obviously take the [sic] out...
Assuming you want to delete the whole group where the sum is > 10000
;with cte as
(
select sum(localamount) over
(partition by siteid, tillid, transid,shiftid) as l,
* from table1
)
delete from cte where l>10000