how to set formula in column - sql

I have problem with setting the formula in particular field. I am using sql database have been added from->add new item->sql database. I have orders table with following field:
ordernum primary key
orderdate smalldatetime,
custemail varchar(50),
subtotal money,
salestax money,
shipping money,
total AS(subtotal+salestax+shipping)
How to set this total formula, there is no datatype mentioned in total field.
Thanks,
Habib

This example should illustrate what you are looking to achieve.
create table #table
(
ordernum int identity(1,1) primary key,
orderdate smalldatetime,
custemail varchar(50),
subtotal money,
salestax money,
shipping money,
total AS(subtotal+salestax+shipping)
)
insert into #table
(
orderdate,
custemail,
subtotal,
salestax,
shipping
)
select
getDate(),
'some#email.com',
1.00,
1.00,
1.00
select * from #table
drop table #table
cheers, John

Related

Trying to aggregate quantities across multiple tables SQL

I'm building a stock synch which compares product quantities from three different tables:
EWM
USOR
DSOR
The maths behind the stock synch is EWM - (USOR + DSOR) = Misalignment
The EWM table divides the product quantities by the different storage types that products are held in which necessitates the need to have the quantities aggregated before being used in the end query but I keep getting the wrong figure after aggregating the quantities so I think I'm doing something wrong but I can't seem to spot it.
Here's my code:
--Table for EWM Data--
create table EWM(
Date_loaded date,
Sap_code bigint,
Product_description varchar(100),
Location nvarchar(50),
Storage_type varchar(50),
Quantity int,
Sap_batch nvarchar(50),
Expiry_date date,
Stock_type varchar(50)
);
--Table for USOR Data--
create table USOR(
Date_Loaded date,
Sap_Code bigint,
Product_description varchar(100),
Pack nvarchar(50),
Cost_price decimal,
Trade_price decimal,
Stock int,
Location nvarchar(50),
);
--Table for DSOR--
create table DSOR(
Date_Loaded datetime,
Sap_code bigint,
Product_description varchar(100),
Bin_location nvarchar(50),
Location varchar(50),
Pack nvarchar(50),
Units int,
Total int,
);
--Table for difference between EWM and USOR+DSOR--
create table Misalignment
(
Date_loaded date,
Sap_code bigint,
Product_description varchar(100),
Pack nvarchar(50),
Location nvarchar(50),
Total_difference int
)
--Inserting data in Misalingment using query from USOR, USOR_DSOR, EWM --
WITH x AS (
SELECT SAP_Code, Quantity
FROM EWM
GROUP BY SAP_Code, Quantity
)
insert into Misalignment(Date_Loaded, Sap_code, Product_description, Location, Total_difference)
Select USOR.Date_Loaded, USOR.Sap_Code, USOR.Product_description, USOR.Location, sum(x.Quantity - (USOR.stock + DSOR.Total))as Total_difference
FROM x
INNER JOIN USOR ON x.SAP_Code = USOR.SAP_Code
INNER JOIN DSOR ON x.SAP_Code = DSOR.SAP_Code
group by USOR.Date_Loaded, USOR.Sap_Code, USOR.Product_description, USOR.Pack, USOR.Location
select * from Misalignment
where Total_difference > 0
order by Total_difference
desc
I ran the query below to test my code out:
select * from ewm
where Date_loaded = '2020-10-05' and Sap_code = 1002945
select * from DSOR
where Sap_code = 1002945 and Date_loaded= '2020-10-05'
select * from Misalignment
where Sap_code = 1002945 and Date_loaded = '2020-10-05'
This was the result:
The actual result should be 0.
Any suggestions?

How to properly generate big random SQL data respecting primary and foreign keys?

Recently I've got a task to generate big testing database built upon this E-R diagram : https://i.stack.imgur.com/I2kr9.png
I need to have over 300,000 rows (combined) and it was easy generating tables Customer, Supplier and Product via Excel by using its random functions but I'm out of idea how to properly create Order and OrderItem tables since I need to multiply every UnitPrice and Quantity from multiple rows (with matching IDs) in order to get TotalAmount which is located in the other table, and of course every PK and FK need to completely match.
I know it's a dummy question but any small tip would be helpful, it doesn't matter if I need to create directly through SQL scripts, Excel or any other way.
Thank you in advance!
:) Check out the sample Northwind database. It already has those tables in it. Field names and count do not match 1-to-1 but easy to edit.
All you need is to proliferate the rows with simple inserts. ie: Double the customers:
insert into customers (FirstName, LastName, City, Country)
select FirstName+'2', LastName+'2', City, Country
from Customers;
300 K rows is not big at all, it is small in fact.
PS: I assumed you would change Id for customers to an int identity column. In Northwind it is character data.
EDIT: Code I promised. It is ugly I know:
-- Create a tally table
DECLARE #tally TABLE (n INT);
INSERT INTO #tally (n)
SELECT TOP(300000) ROW_NUMBER() OVER (ORDER BY t1.Object_ID)
FROM Master.sys.All_Columns t1
CROSS JOIN Master.sys.All_Columns t2;
-- 300K in total. But let's say we want 300K order items
-- Aprx. 1000 customers, each with 50 orders * 6 items per order
create table Customers (Id int identity primary key,
FirstName varchar(15),
LastName varchar(15),
City varchar(15),
Country varchar(15),
Phone varchar(15) );
create table Orders (Id int identity primary key,
OrderDate datetime,
OrderNumber int,
CustomerId int foreign key references Customers(Id),
TotalAmount money null);
create table Suppliers (id int identity primary key,
CompanyName varchar(15),
ContactName varchar(15),
ContactTitle varchar(15),
City varchar(15),
Country varchar(15),
Phone varchar(15),
Fax varchar(15));
create table Products (Id int identity primary key,
ProductName varchar(50),
SupplierId int foreign key references Suppliers(Id),
UnitPrice money,
Package varchar(20),
IsDiscontinued bit);
create table OrderItems (Id int identity primary key,
OrderId int foreign key references Orders(Id),
ProductId int foreign key references Products(Id),
UnitPrice money,
Quantity int);
INSERT INTO Customers
(
FirstName,
LastName,
City,
Country,
Phone
)
SELECT top 1000 'FirstName'+CAST(n AS VARCHAR(6)),
'LastName'+CAST(n AS VARCHAR(6)),
'City'+CAST(n%10 AS VARCHAR(6)),
'Country'+CAST(n%100 AS VARCHAR(6)),
'Phone'+cast(n as varchar(6))
from #tally;
insert into Orders (OrderDate, OrderNumber, CustomerId)
select t.d, t.n, c.Id
from customers c
cross apply (select top(50) n, dateadd(day, -n, getdate()) from #tally) t(n, d);
insert into Suppliers (CompanyName,
ContactName,
ContactTitle,
City,
Country,
Phone,
Fax)
SELECT top 10 'Company'+CAST(n AS VARCHAR(6)),
'Contact'+CAST(n AS VARCHAR(6)),
'Title'+CAST(n AS VARCHAR(6)),
'City'+CAST(n%10 AS VARCHAR(6)),
'Country'+CAST(n%100 AS VARCHAR(6)),
'Phone'+cast(n as varchar(6)),
'Fax'+cast(n as varchar(6))
from #tally;
with ts(n, sId) as (
select t.n, s.Id
from
(SELECT top(500) n from #tally) t, Suppliers s
)
insert into Products (ProductName,
SupplierId,
UnitPrice,
Package,
IsDiscontinued)
SELECT top(5000) 'Product'+CAST(n AS VARCHAR(6)),
sId,
n * 10,
'Package'+CAST(n%5 AS VARCHAR(6)),
case when n%1500 = 0 then 1 else 0 end
from ts order by newid();
with pdata (oid, pid) aS (
select top(300*1000)
abs(cast(checksum(newid()) as bigint)) % 50000 + 1,
abs(cast(checksum(newid()) as bigint)) % 5000 + 1
from #tally
order by newId())
insert into OrderItems
(OrderId,
ProductId,
UnitPrice,
Quantity)
select d.oid, d.pid, p.UnitPrice, abs(cast(checksum(newid()) as bigint)) % 20 + 1
from pData d inner join Products p on d.pid = p.id
order by d.oid, d.pid;

Is there any way to speed up this query capturing customer information? Is there a better method for this data?

I have a long query but I'll try to break it down into it's parts.
First, there are the variables,
DECLARE #LocalCompanyCode VARCHAR(5)
SET #LocalCompanyCode = '09'
DECLARE #LocalDivisionCode VARCHAR(5)
SET #LocalDivisionCode = '001'
DECLARE #CustomerBaseFromDate DATETIME --CustomerBase
SET #CustomerBaseFromDate = '1/1/2019'
DECLARE #CustomerBaseToDate DATETIME
SET #CustomerBaseToDate = '5/30/2019'
DECLARE #RecurringBaseFromDate DATETIME --Recurring Base
SET #RecurringBaseFromDate = '1/1/2018'
DECLARE #RecurringBaseToDate DATETIME
SET #RecurringBaseToDate = '1/1/2019'
DECLARE #LifetimeBaseFromDate DATETIME --Lifetime Base
SET #LifetimeBaseFromDate = '1/1/2015'
DECLARE #LifetimeBaseToDate DATETIME
SET #LifetimeBaseToDate = '1/1/2018'
Company Code and Division Code select which Company we are running this query for. Customer Base will be the base of customers that we will be looking at. So in our example, it is the past 5 months.
So for all customers in the past 5 months, we will look in our Recurring Base. This is modular so we can control what time before we consider a customer "lost". We will compare against the recurring base and see how many customers are new customers who only ordered once over the customer and recurring base, and how many customers are recurring customers, those who ordered multiple times over those periods.
Then we will also have our Lifetime base. We will check customers who ordered once or more in our lifetime base, did not order in our recurring base, but did order again in our 5 months customer base. This calculates how many customers are "Reactivated" or were considered lost but bought with us again recently.
Then I declare the four tables
DECLARE #FullBase TABLE
(
Date_Created DATE,
Company_Code VARCHAR(2),
Division_Code VARCHAR(3),
Invoice_Number VARCHAR(50),
CUST_PO VARCHAR(50),
Total_Quantity NUMERIC,
TotalPrice MONEY,
City VARCHAR(50),
State VARCHAR(50),
Zip VARCHAR(50),
CountryCode VARCHAR(50),
Month NUMERIC,
CustomerEmail VARCHAR(MAX),
OrderCountBase NUMERIC,
TotalOrdersBase NUMERIC
)
DECLARE #LifetimeBase TABLE
(
Date_Created DATE,
Company_Code VARCHAR(2),
Division_Code VARCHAR(3),
Invoice_Number VARCHAR(50),
CUST_PO VARCHAR(50),
Total_Quantity NUMERIC,
TotalPrice MONEY,
City VARCHAR(50),
State VARCHAR(50),
Zip VARCHAR(50),
CountryCode VARCHAR(50),
Month NUMERIC,
CustomerEmail VARCHAR(MAX),
OrderCountLifetimeBase NUMERIC,
TotalOrdersLifetimeBase NUMERIC
)
DECLARE #RecurringBase TABLE
(
Date_Created DATE,
Company_Code VARCHAR(2),
Division_Code VARCHAR(3),
Invoice_Number VARCHAR(50),
CUST_PO VARCHAR(50),
Total_Quantity NUMERIC,
TotalPrice MONEY,
City VARCHAR(50),
State VARCHAR(50),
Zip VARCHAR(50),
CountryCode VARCHAR(50),
Month NUMERIC,
CustomerEmail VARCHAR(MAX),
OrderCountRecurringBase NUMERIC,
TotalOrdersRecurringBase NUMERIC
)
DECLARE #CustomerBase TABLE
(
Date_Created DATE,
Company_Code VARCHAR(2),
Division_Code VARCHAR(3),
Invoice_Number VARCHAR(50),
CUST_PO VARCHAR(50),
Total_Quantity NUMERIC,
TotalPrice MONEY,
City VARCHAR(50),
State VARCHAR(50),
Zip VARCHAR(50),
CountryCode VARCHAR(50),
Month NUMERIC,
CustomerEmail VARCHAR(MAX),
OrderCountCustomerBase NUMERIC,
TotalOrdersCustomerBase NUMERIC
)
Then I insert all of our customers into the "FullBase", from the beginning of the Lifetime Base, to the end of the Customer Base
INSERT INTO #FullBase
SELECT Orders.Date_Created
,Orders.Company_Code
,Orders.Division_Code
,Orders.Invoice_Number
,Orders.CUST_PO
,Orders.Total_Quantity
,Orders.Total
,Orders.City
,Orders.State
,Orders.Zip
,Orders.CountryCode
,Orders.Month
,Orders.CustomerEmail
,Row_Number() over (partition by CustomerEmail order by Date_Created asc) OrderCountBase
,Count(*) over (partition by CustomerEmail) TotalOrdersBase
FROM(
Select
CONVERT(Date, OrderCreated) Date_Created
,CONCAT ('0', LEFT(OrderName,1)) Company_Code
,CONCAT ('00', RIGHT(LEFT(OrderName,2),1)) Division_Code
,InvoiceNumber Invoice_Number
,OrderName CUST_PO
,1 Total_Quantity
,TotalPrice Total
,ShippingCity City
,CASE WHEN ShippingCountryCode <> 'US' THEN 'INT' ELSE ShippingProvinceCode END State
,ShippingZip Zip
,ShippingCountryCode CountryCode
,Month( OrderCreated) Month
,Email CustomerEmail
From [SHOPIFY].[shopify_moret].[dbo].orderwrappers O
Where CONVERT(Date, O.OrderCreated) >= Convert(datetime, '05/29/2019')
AND CONCAT ('0', LEFT(O.OrderName,1)) = #LocalCompanyCode--'09'
AND CONCAT ('00', RIGHT(LEFT(O.OrderName,2),1)) = #LocalDivisionCode --'001'
UNION
Select
Archive.Date_Created
,Archive.Company_Code
,Archive.Division_Code
,Archive.Invoice_Number
,('91'+Archive.CUST_PO) CUST_PO
,Archive.Total_Quantity
,Archive.Total
,Archive.City
,Archive.State
,Archive.Zip
,Archive.Country
,Archive.Month
,Archive.CustomerEmail
FROM SpraygroundArchivedOrders Archive
Where Archive.Date_Created < Convert(datetime, '05/29/2019')
) Orders
Where Orders.Date_Created BETWEEN #LifetimeBaseFromDate AND #CustomerBaseToDate
Here is an example of how the data looks like:
https://docs.google.com/spreadsheets/d/1wnjVHcPHHnugywa7Qz-aqctaD4cDI5DxehNna0TyaFU/edit?usp=sharing
Then I use this full base to populate the three other tables with orders from their range.
INSERT INTO #LifetimeBase
SELECT
F.Date_Created
,F.Company_Code
,F.Division_Code
,F.Invoice_Number
,F.CUST_PO
,F.Total_Quantity
,F.TotalPrice
,F.City
,F.State
,F.Zip
,F.CountryCode
,F.Month
,F.CustomerEmail
,Row_Number() over (partition by CustomerEmail order by Date_Created asc) OrderCountLifetimeBase
,Count(*) over (partition by CustomerEmail) TotalOrdersLifetimeBase
FROM #FullBase F
Where F.Date_Created BETWEEN #LifetimeBaseFromDate AND #LifetimeBaseToDate
INSERT INTO #RecurringBase
SELECT
F.Date_Created
,F.Company_Code
,F.Division_Code
,F.Invoice_Number
,F.CUST_PO
,F.Total_Quantity
,F.TotalPrice
,F.City
,F.State
,F.Zip
,F.CountryCode
,F.Month
,F.CustomerEmail
,Row_Number() over (partition by CustomerEmail order by Date_Created asc) OrderCountRecurringBase
,Count(*) over (partition by CustomerEmail) TotalOrdersRecurringBase
FROM #FullBase F
Where F.Date_Created BETWEEN #RecurringBaseFromDate AND #RecurringBaseToDate
INSERT INTO #CustomerBase
SELECT
F.Date_Created
,F.Company_Code
,F.Division_Code
,F.Invoice_Number
,F.CUST_PO
,F.Total_Quantity
,F.TotalPrice
,F.City
,F.State
,F.Zip
,F.CountryCode
,F.Month
,F.CustomerEmail
,Row_Number() over (partition by CustomerEmail order by Date_Created asc) OrderCountCustomerBase
,Count(*) over (partition by CustomerEmail) TotalOrdersCustomerBase
FROM #FullBase F
Where F.Date_Created BETWEEN #CustomerBaseFromDate AND #CustomerBaseToDate
If I just select * from an of the bases, including the full base, the query only takes a few seconds to run and returns 140,000 rows (for the #FullBase) of all customer orders.
However, the final part of this query, which is the part that runs, takes 10 minutes to run for my 6 months of customers
SELECT
CC.CustomerEmail
,CC.TotalOrdersCustomerBase
,RC.TotalOrdersRecurringBase
,LC.TotalOrdersLifetimeBase
From
(
SELECT DISTINCT
C.CustomerEmail
,C.TotalOrdersCustomerBase
FROM
#CustomerBase C
) CC
LEFT JOIN
(
SELECT DISTINCT
R.CustomerEmail
,R.TotalOrdersRecurringBase
FROM
#RecurringBase R
) RC ON CC.CustomerEmail = RC.CustomerEmail
LEFT JOIN
(
SELECT DISTINCT
L.CustomerEmail
,L.TotalOrdersLifetimeBase
FROM
#LifetimeBase L
) LC ON CC.CustomerEmail = LC.CustomerEmail
Does anyone have any tips for me at all? Is there any better way to do this?

2 SQL queries in one stored procedure

Can I run 2 queries in one stored procedure ?
CREATE PROCEDURE AddProd
#Store_Name varchar(50),
#Price int,
#Prod_Name varchar(50),
#Qty int,
#ProductDescription varchar(50),
#RatingSum int,
#RatingCount int,
#ProductImage varchar(50),
#Prod_Date date,
AS
BEGIN
SELECT S.Store_ID
FROM Store S
WHERE StoreName=#StoreName
INSERT INTO Product (Store_ID, Price, Prod_Name, Qty, ProductDescription, RatingSum, RatingCount, ProductImage, Prod_Date)
VALUES (S.Store_ID, #Price, #Prod_Name, #Qty, #ProductDescrpition, #RatingSum, #RatingCount, #ProductImage, #Prod_Date)
END
GO
For this code above, I want retrieve a STORE_ID by giving the STORE_NAME the user give as a parameter.
I want to use this STORE_ID in the INSERT statement.
Can I do this ?!
AKA, is the S.store_ID returned from the first query, the same as that one I used in "Values" ?
Technically, you can do this in a single query:
INSERT INTO Product
(Store_ID, Price, Prod_Name, Qty, ProductDescription, RatingSum, RatingCount, ProductImage, Prod_Date)
SELECT S.Store_ID, #Price,#Prod_Name,#Qty,#ProductDescription,#RatingSum,#RatingCount,#ProductImage,#Prod_Date
FROM Store S
WHERE StoreName=#StoreName
I don't have test data handy to check, but you may have to give the appropriate names to each of the columns in the select clause from that query, instead of just the variable names. The only other reason this might not work is if you also wanted to return the selected storeID from the stored procedure, but even in that case you can just add an OUTPUT clause:
INSERT INTO Product
(Store_ID, Price, Prod_Name, Qty, ProductDescription, RatingSum, RatingCount, ProductImage, Prod_Date)
OUTPUT S.Store_ID
SELECT S.Store_ID, #Price,#Prod_Name,#Qty,#ProductDescription,#RatingSum,#RatingCount,#ProductImage,#Prod_Date
FROM Store S
WHERE StoreName=#StoreName
But for the title question, the answer is affirmative; you can execute multiple statements inside a single stored procedure.
unless you want to return the storeID from the sp remove that query and put it into the insert
INSERT INTO Product (Store_ID,Price,Prod_Name,Qty,ProductDescription,RatingSum,RatingCount,ProductImage,Prod_Date)
values (
(SELECT S.Store_ID FROM Store S WHERE StoreName=#StoreName),
#Price,#Prod_Name,#Qty,#ProductDescrpition,#RatingSum,#RatingCount,#ProductImage,#Prod_Date)
If StoreID is unique for each store name you can store it in a variable and use it in your insertion
CREATE PROCEDURE AddProd
#Store_Name varchar(50),
#Price int,
#Prod_Name varchar(50),
#Qty int,
#ProductDescription varchar(50),
#RatingSum int,
#RatingCount int,
#ProductImage varchar(50),
#Prod_Date date,
AS
BEGIN
DECLARE #StoreID [DataType]
SELECT #StoreID = S.Store_ID
FROM Store S
WHERE StoreName=#StoreName
INSERT INTO Product (Store_ID,Price,Prod_Name,Qty,ProductDescription,RatingSum,RatingCount,ProductImage,Prod_Date)
values (#StoreID,#Price,#Prod_Name,#Qty,#ProductDescrpition,#RatingSum,#RatingCount,#ProductImage,#Prod_Date)
END
GO
In any scenario you can use the following
CREATE PROCEDURE AddProd
#Store_Name varchar(50),
#Price int,
#Prod_Name varchar(50),
#Qty int,
#ProductDescription varchar(50),
#RatingSum int,
#RatingCount int,
#ProductImage varchar(50),
#Prod_Date date,
AS
BEGIN
INSERT INTO Product (Store_ID,
Price,
Prod_Name,
Qty,
ProductDescription,
RatingSum,
RatingCount,
ProductImage,
Prod_Date
)
SELECT
S.Store_ID
#StoreID,
#Price,
#Prod_Name,
#Qty,
#ProductDescrpition,
#RatingSum,
#RatingCount,
#ProductImage,
#Prod_Date
FROM Store S
WHERE StoreName=#StoreName
END
GO

SQL Server 2005 Query

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.