Write a SQL Stored Procedure (pr_GetOrderSummary) to return a summary of orders from the data in the Northwind database. You may NOT use Dymanic SQL to solve the problem.
The results should be able to be filtered by specifying parameters:
• Date of the Order (#StartDate and #EndDate)
• Nullable Parameter to filter for a specific Employee (#EmployeeID)
• Nullable Parameter to filter for a specific Customer (#CustomerID)
The columns to be returned are:
• EmployeeFullName (TitleOfCourtesy + FirstName + LastName)
• Shipper CompanyName
• Customer CompanyName
• NumberOfOders
• Date
• TotalFreightCost
• NumberOfDifferentProducts
• TotalOrderValue
The results should be grouped by:
• Order Day (i.e. grouped by day)
• Employee
• Customer
• Shipper
Some helpful tests:
`exec pr_GetOrderSummary #StartDate='1 Jan 1996', #EndDate='31 Aug 1996', #EmployeeID=NULL , #CustomerID=NULL`
`exec pr_GetOrderSummary #StartDate='1 Jan 1996', #EndDate='31 Aug 1996', #EmployeeID=5 , #CustomerID=NULL`
`exec pr_GetOrderSummary #StartDate='1 Jan 1996', #EndDate='31 Aug 1996', #EmployeeID=NULL , #CustomerID='VINET'`
`exec pr_GetOrderSummary #StartDate='1 Jan 1996', #EndDate='31 Aug 1996', #EmployeeID=5 , #CustomerID='VINET'`
CREATE PROCEDURE pr_GetOrderSummary
#StartDate datetime,
#EndDate datetime,
#EmployeeID int = NULL,
#CustomerID varchar(50) = NULL
AS
BEGIN
Select
o.OrderID,
ei.TitleOfCourtesy + ' ' + ei.FirstName + ' ' + ei.LastName AS 'EmployeeFullName',
sid.CompanyName as 'Shipper CompanyName',
cid.CompanyName as 'Customer CompanyName',
CONVERT(VARCHAR(11), o.OrderDate, 106) AS 'Date',
o.Freight,
od.Quantity,
(od.UnitPrice * od.Quantity) * (1 - od.Discount) AS 'TotalOrderValue'
FROM Orders o
Inner Join Shippers sid
on o.ShipVia = sid.ShipperID
Inner Join Employees ei
on o.EmployeeID = ei.EmployeeID
Inner Join Customers cid
on o.CustomerID = cid.CustomerID
Inner Join [Order Details] od
on o.OrderID = od.OrderID
WHERE
o.OrderDate >= #StartDate and
o.OrderDate <= #EndDate and
(o.EmployeeID is null or o.EmployeeID = #EmployeeID) AND
(o.CustomerID is null OR o.CustomerID = #CustomerID);
END
The above is my stored procedure that i created it works when all the parameters are included but it does work when one ore more of the parameters are NULL
Related
I have a search engine where the user will specify multiple conditions and based on these conditions i will return a datatable.
The problem I am facing is that the conditions are not respected and I am getting wrong results.
I have tried to test every condition alone, it is working, but when I put all the conditions together, I am getting unexpected results.
USE [Tenant Management]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[RentSearchEngine]
#sqm INT,
#category INT,
#shortRentPrice MONEY,
#longRentPrice MONEY,
#fromDate DATETIME,
#toDate DATETIME
AS
BEGIN
SET NOCOUNT ON;
SELECT
c.[Name], cate.[Channel Category], tp.type, st.Status, c.Surface,
g.GOVERNATOR + ' ' + d.District + ' ' + cit.City + ' ' as [Address],
c.[Short term Price per night] AS [Short term monthly amount],
c.[Long term price per month] AS [Long term monthly amount],
c.[Selling Price]
FROM
[dbo].[Channel] c
INNER JOIN
[dbo].[Governator] g ON c.[Governator ID] = g.ID
INNER JOIN
[dbo].[District] d ON c.[District ID] = d.ID
INNER JOIN
[dbo].[City] cit ON c.[City ID] = cit.id
INNER JOIN
[dbo].[Channel_Category] cate ON c.[Channel Category ID] = cate.ID
INNER JOIN
[dbo].[Channel_Type] tp ON c.[Channel Type] = tp.id
INNER JOIN
[dbo].[Channel_Status] st ON c.[Channel Status] = st.ID
LEFT JOIN
[dbo].[Reservations] r ON c.[ID] = r.[Channel ID]
WHERE
c.[Channel Status] = '5'
AND c.[Channel Type] = '1'
AND c.[Channel Category ID] = #category OR #category IS NULL
AND c.Surface BETWEEN #sqm * 0.85 AND #sqm * 1.15 OR #sqm IS NULL
AND c.[Long term price per month] BETWEEN #longRentPrice * 0.85
AND #longRentPrice * 1.15 OR #longRentPrice IS NULL
AND c.[Short term Price per night] BETWEEN #shortRentPrice * 0.85
AND #shortRentPrice * 1.15 OR #shortRentPrice IS NULL
AND (r.[Actual Date in] > #fromDate AND r.[Actual Date out] > #toDate)
AND (r.[Actual Date in] < #fromDate AND r.[Actual Date out] < #toDate)
END
The current result is:
fdfd Residential apatment For Rent Available 500 Mont Liban Baabda Ain El Remmaneh 1287182.00 28712.00 128712.00
When executing the stored procedure as follow:
DECLARE #return_value int
EXEC #return_value = [dbo].[RentSearchEngine]
#sqm = 40000,
#category = 1,
#shortRentPrice = 5,
#longRentPrice = 4,
#fromDate = NULL,
#toDate = NULL
SELECT 'Return Value' = #return_value
I think the key issue is the operator preference for AND & OR.
Bear in mind that AND has higher preference than OR.
In these complex conditions is a good practise to ensure the order with parenthesis.
I will write what I understand you are trying to achieve but ensure you use the parenthesis in the order you require:
where c.[Channel Status] = '5'
and c.[Channel Type] = '1'
and (c.[Channel Category ID] =#category or #category IS NULL)
and (c.Surface between #sqm*0.85 and #sqm*1.15 or #sqm IS NULL)
and (c.[Long term price per month] between #longRentPrice*0.85 and #longRentPrice*1.15 or #longRentPrice IS NULL)
and (c.[Short term Price per night] between #shortRentPrice*0.85 and #shortRentPrice*1.15 or #shortRentPrice IS NULL)
and (r.[Actual Date in] > #fromDate and r.[Actual Date out] > #toDate)
and (r.[Actual Date in] < #fromDate and r.[Actual Date out] < #toDate)
I have the following SQL statement which shows me transactions from a Point Of Sale system. I would like to calculate the total sum of (MoneyIn - MoneyOut) columns but is a bit beyond me and show this as a single value. I am sure its a simple mod to this.
DECLARE #StartDate DateTime;
DECLARE #EndDate DateTime;
DECLARE #SearchTerm NVARCHAR(200);
SET #SearchTerm = '%widget1%'
SET #StartDate = '2018-05-01 00:00:00'
SET #EndDate = DATEADD(month, 1, #StartDate)
SELECT TOP (500)
t.TransactionDate,
t.MoneyIn,
t.MoneyOut,
t.Description,
p.PaymentMethodName,
t.TransactionRef,
c.SalesItems,
COUNT(*) AS Occurrences
FROM
Transactions t
LEFT JOIN
Tills tl ON t.TillId = tl.TillId
INNER JOIN
PaymentMethods p ON t.PaymentMethodId = p.PaymentMethodsID
INNER JOIN
Membership m ON t.UserId = m.UserId
CROSS APPLY
(SELECT
STUFF((SELECT ',' + CAST(tp.Description AS VARCHAR(100))
FROM TransactionsPosLines tp
WHERE t.TransactionId = tp.TransactionId
FOR XML PATH('')), 1, 1, '') AS SalesItems) c
WHERE
t.TransactionDate >= #StartDate
AND t.TransactionDate <= #EndDate
AND (t.PaymentTypeId = 1)
AND SalesItems LIKE #SearchTerm
GROUP BY
t.TransactionDate,
t.MoneyIn,
t.MoneyOut,
t.Description,
p.PaymentMethodName,
t.TransactionRef,
m.Username,
c.SalesItems
ORDER BY
COUNT(*) DESC
Output:
2018-05-01 17:23:23.243 9.99 0.00 Sale - Card Card Shop Sale Grab n Go item 1
2018-05-08 13:15:04.577 10.00 -0.01 Sale - Cash Total: 9.99 Cash Shop Sale Grab n Go item 1
2018-05-10 14:08:47.120 7.99 0.00 Sale - Card Card Shop Sale Grab n Go item,Discount 1
and I want to show:
Total Sales: 27.97
(Sum of MoneyIn - Sum of MoneyOut)
So that this question has an answer
(edit: added the actual SQL I used to resolve this which gave the single result I was looking for), thanks for help:
Select top (10000)
sum(t.MoneyIn) - sum(t.MoneyOut) as 'moneytotal'
From
Transactions t
LEFT JOIN Tills tl
ON t.TillId = tl.TillId
INNER JOIN PaymentMethods p
ON t.PaymentMethodId = p.PaymentMethodsID
INNER JOIN Membership m
On t.UserId = m.UserId
CROSS APPLY
(SELECT STUFF(
(SELECT ',' + CAST(tp.Description AS VARCHAR(100))
FROM
TransactionsPosLines tp
WHERE t.TransactionId = tp.TransactionId
FOR XML PATH(''))
,1,1,'') as SalesItems) c
Where
t.TransactionDate >= #StartDate
AND t.TransactionDate <= #EndDate
AND (t.PaymentTypeId = 1)
and SalesItems LIKE #SearchTerm
Order By
count(*) Desc
GO
I have created a stored procedure to get data. In this stored procedure, I have returned 1 table and table stores the data above 1 lakh + data. So right now I have run the stored procedure that time I get the data in above 1 minute time take. I want just with in 1 second get data. I have set also SET NOCOUNT ON; and also create missing index. Still I am getting same time for the get data.
This is my query:
DECLARE #CurMon int
DECLARE #year nvarchar(max)
SELECT #CurMon = month(getdate())
SELECT #year = year(getdate())
SELECT
FORMAT(dateadd(MM, T.i, getdate()),'MMM-yy') AS DateColumn,
ISNULL(uf.TotalCount, 0) as TotalCount
FROM
(VALUES (12-#CurMon),(11-#CurMon),(10-#CurMon),(9-#CurMon),(8-#CurMon),(7-#CurMon),(6-#CurMon), (5-#CurMon), (4-#CurMon), (3-#CurMon), (2-#CurMon), (1-#CurMon)) AS T(i)
OUTER APPLY
(SELECT DISTINCT
COUNT(datepart(MM,UF.InsertDateTime)) OVER (partition by datepart(MM,UF.InsertDateTime)) AS TotalCount
FROM dbo.UserFollowers UF
INNER JOIN dbo.Users U on U.UserId = UF.FollowerId
WHERE DATEDIFF(mm,UF.InsertDateTime, DATEADD(mm, T.i, GETDATE())) = 0 and UF.IsFollowed = 1
) uf
order by DATEPART(MM,convert(datetime,FORMAT(dateadd(MM, T.i, getdate()),'MMMM') +'01 '+#year,110))
i am also try some other query for the improve speed of query but still i am getting same time. here this query also print.
declare #StartDate datetime = dateadd(year , datediff(year , 0, getdate() ) , 0)
declare #tempT2 table
(
MNo int,
[Month] datetime,
NextMonth datetime)
;with Months as (
select top (12)
MNo = row_number() over (order by number)
,[Month] = dateadd(month, row_number() over (order by number) -1, #StartDate)
, NextMonth = dateadd(month, row_number() over (order by number), #StartDate)
from master.dbo.spt_values
)
insert into #tempT2
select * from Months
select
m.MNo
, Month = format(m.Month, 'MMM-yy')
, tally = count(UF.InsertDateTime)
from #tempT2 m
left join dbo.UserFollowers UF
INNER JOIN dbo.Users U on U.UserId = UF.FollowerId
on UF.InsertDateTime >= m.Month
and UF.InsertDateTime < m.NextMonth where UF.IsFollowed = 1
group by m.MNo,format(m.Month, 'MMM-yy')
order by MNo
here this is my both query i have try but still i am not getting success for the improve the speed of the query. and sorry but i can not see here my execution plan of the query actually i have not permission for that.
You can gain a little bit of performance by switching to a temporary table instead of a table variable, and by getting rid of format():
declare #StartDate datetime = dateadd(year , datediff(year , 0, getdate() ) , 0)
create table #Months (
MNo int not null primary key
, Month char(6) not null
, MonthStart datetime not null
, NextMonth datetime not null
)
;with Months as (
select top (12)
MNo = row_number() over (order by number)
, MonthStart = dateadd(month, row_number() over (order by number) -1, #StartDate)
, NextMonth = dateadd(month, row_number() over (order by number), #StartDate)
from master.dbo.spt_values
)
insert into #Months (MNo, Month, MonthStart, NextMonth)
select
MNo
, Month = stuff(convert(varchar(9),MonthStart,6),1,3,'')
, MonthStart
, NextMonth
from Months;
select
m.MNo
, m.Month
, tally = count(UF.InsertDateTime)
from #tempT2 m
inner join dbo.Users U
on UF.InsertDateTime >= m.MonthStart
and UF.InsertDateTime < m.NextMonth
inner join dbo.UserFollowers UF
on U.UserId = UF.FollowerId
and UF.IsFollowed = 1
group by
m.MNo
, m.Month
order by MNo
After that, you should evaluate the execution plan to determine if you need a better indexing strategy.
If you still need it to go faster, you could create an actual calendar table and look into creating an indexed view. An indexed view can be a chore get it to behave correctly depending on your sql server version, but will be faster.
Reference:
format performance - Aaron Bertrand
What is the difference between a temp table and table variable in SQL Server? - Answer by Martin Smith
When should I use a table variable vs temporary table in sql server? - Answer by Martin Smith
Indexed Views and Statistics - Paul White
Generate a set or sequence without loops - 2 - Aaron Bertrand
The "Numbers" or "Tally" Table: What it is and how it replaces a loop - Jeff Moden
Creating a Date Table/Dimension in sql Server 2008 - David Stein
Calendar Tables - Why You Need One - David Stein
Creating a date dimension or calendar table in sql Server - Aaron Bertrand
I have a query that I use to generate statements that shows Amount Due for the month which is calculated based on date parameters- #StartDate and #EndDate
Included in the statement, I would like to add the Amount Due from the previous month (Previous Month's Balance owing) for a date range #StartDate - 30 to #EndDate - 30. What would be the code to run that?
My code:
set nocount on
Declare #S AS DateTime = ISNULL(#StartDate,DateAdd(d,-60,GETDATE()))
Declare #anum as nvarchar(8) = ISNULL(#panum,'25991275')
Declare #E AS DateTime = ISNULL(#EndDate,DateAdd(d,-0,GETDATE()))
SELECT A.AccountNumber
,C.FirstName + ' ' + C.LastName CustName
,[InvoiceNumber]
,[StatementDate]
,[NewCharges]
,[AmountDue]
,[Charges]
,[AccountFee]
,[Interest]
,[Payments]
,[Refunds]
,[DueDate]
FROM [StatementSummary] S
INNER JOIN Account A ON S.AccountID = A.Accountid
INNER JOIN Contact C ON A.AccountId = C.AccountId
WHERE A.AccountNumber = #anum
AND StatementDate >= #S
AND StatementDate <= #E
ORDER BY StatementDate DESC
I was thinking of making another Dataset to run the following code:
SELECT Top 1 AcctBalance
FROM [FinMaster]
WHERE AcctID = #anum
AND BusinessDay >= #S - 30
AND BusinessDay <= #E - 30
ORDER BY AcctBalance DESC
How do I add the date range to back to the previous month's?
If I could add this second code as a line in the first code then I won't need to create a second dataset for my report.
Using OUTER APPLY and EOMonth function to get the Last Month value
Just a logic and not using your fields
declare #reportdate date = getdate()
select a.*, x.field....
from table1 A
OUTER apply ( --- to get last month data, can be null.. similar to left outer join but in a cartesian way of display
select b.field1, b.field2, b....
from table1 B
where
b.product_id = a.product_id and
trans_date
between -- between last month based on the #reportdate
dateadd(day,1,eomonth(dateadd(month,-2,#reportdate))) -- or a.trans_date
and
eomonth(dateadd(month,-1,#reportdate))
) x
where trans_date
between -- your reporting date, can be any date
dateadd(day,1,eomonth(dateadd(month,-1,#reportdate)))
and eomonth(#reportdate)
Need some help getting sales by month for a customer and his agent channel
have a customer table that looks something like this
customer table:
cust_id bigint,
agent_id bigint,
name varchar(200),
customer_level int,
date_signed datetime
a customer level = 1 a customer who can transact; agent_id = null
a customer_level = 2 an agent of a level_1 customer and can transact, level_1 cust_id = level_2 agent_id
Transaction table:
tx_id bigint,
tx_date datetime,
description varchar(200),
amount money,
cust_id bigint
SQL
SELECT datepart(month, t.tx_date) AS TX_MONTH
,CASE
WHEN c.customer_level = 2
THEN a.NAME
ELSE c.NAME
END AS CUSTOMER
,count(t.amount)
,sum(t.amount)
FROM TRANSACTION t
INNER JOIN customer c ON t.cust_id = c.cust_id
LEFT JOIN customer a ON c.agent_id = a.cust_id
WHERE t.tx_date >= '2014-01-01 00:00:00.000'
GROUP BY datepart(month, t.tx_date)
ORDER BY 1 ASC
===
for starters this sql wont work need to group by c.name, a.name
however the intended result will not be realised....which is to get monthly sales by a customer and his agent channel
HELP!
Assumptions I'm making:
You will likely want year as part of the result set in case this report is used across the new year boundary.
You may want to pass parameters to this query, I'm going to use #CustID BIGINT, and #StartDate DATE, #EndDate DATE to help limit rows for performance reasons in the case of large result sets.
You will need to group by the expression used in the select clause.
Try this:
SELECT MONTH(t.tx_date) AS TX_MONTH, YEAR(t.tx_date) AS TX_YEAR
,CASE
WHEN c.customer_level = 2
THEN a.NAME
ELSE c.NAME
END AS CUSTOMER
,count(t.amount)
,sum(t.amount)
FROM TRANSACTION t
INNER JOIN customer c ON t.cust_id = c.cust_id
LEFT JOIN customer a ON c.agent_id = a.cust_id
WHERE CAST(t.tx_date AS DATE) BETWEEN #StartDate AND #EndDate
AND c.cust_id = #CustID
GROUP BY YEAR(t.tx_date), MONTH(t.tx_date)
,CASE
WHEN c.customer_level = 2
THEN a.NAME
ELSE c.NAME
END
ORDER BY YEAR(t.tx_date), MONTH(t.tx_date)
This should give you a result set with Year, Month of transactions the Agent name or Customer Name, for a given customer between two dates.