Exclude certain line depending on a certain condition in t-SQL - sql-server

Firstly, I have created the following stock sales SELECT statement:
SELECT
PostST.TxDate AS TxDate,
PostST.Reference AS InvNum,
Client.Name AS CustomerName,
SalesRep.Code AS SalesRep,
WhseMst.Code AS Whse,
CONCAT (StkItem.Description_1, ' - ',StkItem.Code) AS Item,
_etblLotTracking.cLotDescription AS LotNumber,
CASE
WHEN PostST.TrCodeID = 34 THEN (PostST.Quantity * 1)
WHEN PostST.TrCodeID = 30 THEN (PostST.Quantity * -1)
ELSE 0
END AS QtySold,
CAST
(
CASE
WHEN PostST.TrCodeID = 34 THEN (PostST.Credit / PostST.Quantity)
WHEN PostST.TrCodeID = 30 THEN (PostST.Debit / (PostST.Quantity * -1))
ELSE 0
END
as [money]
)
AS CustomerPrice,
StkItem.ItemGroup AS ItemGroup,
StkItem.ulIIStockType AS StockType,
YEAR (PostST.TxDate) AS Year,
DATENAME (MONTH, (PostST.TxDate)) AS Month
FROM
PostST
INNER JOIN StkItem
ON StkItem.StockLink = PostST.AccountLink
INNER JOIN PostAR
ON PostAR.cAuditNumber = PostST.cAuditNumber
INNER JOIN Client
ON Client.DCLink = PostAR.AccountLink
INNER JOIN SalesRep
ON SalesRep.idSalesRep = PostAR.RepID
INNER JOIN WhseMst
ON WhseMst.WhseLink = PostST.WarehouseID
FULL JOIN _etblLotTracking
ON _etblLotTracking.idLotTracking = PostST.iLotID
WHERE
PostST.TrCodeID IN (34, 30) AND StkItem.ItemGroup <> 'TRAN'
This query is run from Sage Evolution and data is dumped into Excel. From there I manipulate to view each item and the sales history in quantity per month and year.
My problem is as follows:
Within the data that follows, is sometimes a customer who has perhaps stopped buying a particular item. What I need is for this statement to also filter out a line by the following condition:
If the customer does not have a sale for 6 months or more, then that STOCK ITEM must be filtered out for that specific customer.
Thank you!

Related

Not every row is shown in a query

I have a query to calculate the number of transactions and the number of payments in my table. These counts are based on data in separated tables. I need to group the results by year and the week of year. The dates are coming also from a separate table. I add the week values in an union of the payments and transactions in a with clause and do afterwards the calculation.
WITH CHART_DATES
AS (
SELECT DD.CAL_YEAR, DD.WEEK_OF_YEAR
FROM DIM_DATE DD
RIGHT JOIN FACT_PAY_PAYMENT FPP ON DD.ID = FPP.REQUESTED_EXECUTION_DATE_ID
GROUP BY DD.CAL_YEAR, DD.WEEK_OF_YEAR
UNION
SELECT DD.CAL_YEAR, DD.WEEK_OF_YEAR
FROM DIM_DATE DD
RIGHT JOIN FACT_AS_BALANCE FAB ON DD.ID = FAB.BALANCE_DATE_ID
GROUP BY DD.CAL_YEAR, DD.WEEK_OF_YEAR
)
SELECT
CD.WEEK_OF_YEAR,
CD.CAL_YEAR,
COALESCE(COUNT(DISTINCT FPP.ID), 0) AS PAYMENTS,
COALESCE(COUNT(DISTINCT FAB.ACCOUNT_STATEMENT_ID), 0) AS STATEMENTS
FROM FACT_PAY_PAYMENT FPP
RIGHT JOIN DIM_DATE DD ON FPP.REQUESTED_EXECUTION_DATE_ID = DD.ID
FULL JOIN FACT_AS_BALANCE FAB ON DD.ID = FAB.BALANCE_DATE_ID
RIGHT JOIN CHART_DATES CD ON DD.CAL_YEAR = CD.CAL_YEAR
AND DD.WEEK_OF_YEAR = CD.WEEK_OF_YEAR
INNER JOIN DIM_AS_CHARACTERISTICS DAC ON FAB.BALANCE_TYPE_ID = DAC.ID
WHERE DAC.TRANSACTION_INTRADAY_FLAG = 'N'
GROUP BY CD.CAL_YEAR, CD.WEEK_OF_YEAR
I added following data in my tables:
Week 42 of 2015 has 1 payment and 2 transactions
Week 41 of 2015 has 1 payment and 0 transactions
After executing the query, I receive following result:
Wrong result
What is wrong in my query and how to fix it?

Look at data in one date range and count from another date range

I'm stuck on a query where I am trying to get information on just customers that are newly acquired during a certain date range.
I had need to get a list of customers who placed their first order (of all time) in the first 6 months of the year. I then need to get total of their invoices, first invoice date, last invoice date, and count of orders for just the last 6 months.
I used a HAVING clause to ensure that I am just looking at customers that placed their first order in that 6 month period, but since we are past that period now, the total invoice info and order count information would include orders placed after this time. I considered including a restriction in the HAVING clause for the 'last invoice date', but then I am eliminating customers whose first order date was in the 6 month block, but also ordered after that. I'm not sure what to do next and am not having luck finding similar questions. Here is what I have so far:
SELECT c.customer, MAX(c.name) AS Name,
SUM(
CASE WHEN im.debit = 0
THEN im.amount * -1
ELSE im.amount
END
) AS TotalInvoiceAmount,
MIN(
im.date) AS FirstInvoiceDate,
MAX(
im.date) AS LastInvoiceDate,
COUNT(DISTINCT om.[order]) AS OrderCount
FROM invoicem im
INNER JOIN customer c ON im.customer = c.customer
FULL JOIN orderm om ON im.customer = om.customer
WHERE im.amount <> 0
GROUP BY c.customer
HAVING MIN(im.date) BETWEEN '01-01-2015' AND '06-30-2015'
ORDER BY c.customer
You can put the first 6 months qualification as a subquery. This would also work as a CTE
declare #startDate date = dateadd(month,-6,getdate())
SELECT c.customer, MAX(c.name) AS Name,
SUM(
CASE WHEN im.debit = 0
THEN im.amount * -1
ELSE im.amount
END
) AS TotalInvoiceAmount,
MIN(
im.date) AS FirstInvoiceDate,
MAX(
im.date) AS LastInvoiceDate,
COUNT(DISTINCT om.[order]) AS OrderCount
FROM invoice im
INNER JOIN (SELECT customer from invoice
GROUP BY customer
HAVING MIN(date) BETWEEN '01-01-2015'
AND '06-30-2015') im2
ON im.customer = im2.customer
INNER JOIN customer c ON im.customer = c.customer
FULL JOIN orderm om ON im.customer = om.customer
WHERE im.amount <> 0
AND im.date >= #startdate
GROUP BY c.customer
ORDER BY c.customer

ISNULL() for calculated column in Full Join. SQL Server

I'm currently facing a problem when calculating the standard deviation in an SQL Server statement.
My problem: I have two tables.
T1:
Reg_Month
---------
1
2
3
...
T2:
Product Month Consumption
-------------------------------
ProdA 1 200
ProdB 1 10
ProdA 1 300
ProdC 2 100
ProdA 2 200
...
Now what I want is something like this, for calculating my STDEV over a year:
Reg_Month Product Month Sum
---------------------------------
1 ProdA 1 500
1 ProdB 1 10
1 ProdC 1 0
2 ProdA 2 200
2 ProdB 2 0
2 ProdC 2 0
So now I don't need this table to be set up, but I need to calculate the STDEV and AVG of the column "Sum" for each Product. The Problem is to include the NULLS.
This gives me the table I want for ProdA, but with NULLS whenever there was no consumption:
SELECT *
FROM T1
FULL JOIN (SELECT Product, Month, SUM(Consumption) AS Sum,
FROM T2
WHERE (Product = 'ProdA')
GROUP BY Month, Product) sub ON (T1.Reg_Month = T2.Month)`
This gives me the STDEV:
SELECT Stdev(Sum)
FROM
(SELECT *
FROM T1
FULL JOIN (SELECT Product, Month, SUM(Consumption) AS Sum,
FROM T2
WHERE (Product = 'ProdA')
GROUP BY Month, Product) sub ON (T1.Reg_Month = T2.Month)) sub
WHERE Product = 'ProdA'`
But the problem is, that it doesn't give me the correct STDEV for the entire year, if there is a month in which there was no consumption, because the NULLs (that appear due to the join) are ignored.
My approaches:
ISNULL():
SELECT Stdev(Sum)
FROM
(SELECT *
FROM T1
FULL JOIN (SELECT Product, Month, ISNULL(SUM(Consumption), 0) AS Sum,
FROM T2
WHERE (Product = 'ProdA')
GROUP BY Month, Product) sub ON (T1.Reg_Month = T2.Month)) sub
WHERE Product = 'ProdA'`
doesn't work (maybe because the null is generated after the join?)
CASE:
SELECT Stdev(Sum)
FROM
(SELECT *
FROM T1
FULL JOIN (SELECT Product, Month, CASE WHEN SUM(Consumption) IS NULL THEN 0 ELSE Sum(Consumption) END AS Sum,
FROM T2
WHERE (Product = 'ProdA')
GROUP BY Month, Product) sub ON (T1.Reg_Month = T2.Month)) sub
WHERE Product = 'ProdA'
doesn't work (probably the same reason)
I hope I was able to illustrate my example properly. Now, do you have any idea how get the right results for the STDEV?
Your input would be greatly appreciated!
Thanks a lot,
Clemens
if you are only doing one product at a time
SELECT sum(isnull(T2month.MonthSum ,0))
, Stdev(isnull(T2month.MonthSum ,0))
FROM T1
LEFT JOIN
(select Month, sum(Consumption) as MonthSum
from T2
where Product = 'ProdA'
group by Month) T2month
ON T1.Reg_Month = T2month.Month
for all products
SELECT Product.Name
, sum(isnull(T2month.MonthSum ,0))
, Stdev(isnull(T2month.MonthSum ,0))
FROM T1
cross apply
(values ('ProdA'), ('ProdB'), ('ProdC')) Product(Name)
LEFT JOIN
(select Product, Month, sum(Consumption) as MonthSum
from T2
group by Product, Month) T2month
ON T1.Reg_Month = T2month.Month
AND T2month.Product = Product.Name
Group By Product.Name
Hey OP left join does NOT leave out periods
That is what a left join does
select lj.val, isnull(jj.val,0)
from ( values (1), (2), (3), (4) ) lj(val)
left join ( values (1), (2), (4) ) jj(val)
on lj.val = jj.val
order by lj.val

TSQL Subquery effeciency

I am trying to write a subquery in a view to be returned as a column but I am not exactly sure what would give me the most efficient call.
I have View A that gathers a bunch of fields from different tables (one table being Listings that has has a 1 to many relationship with OpenHours) then one of the fields I want it to be from another table (OpenHours) that will only be Today's Open hours field.
The OpenHours table has ListingID, Day (0 based for the day of the week), Hours (text of the open hours such as "8:00am-5:00pm"). Here is what I need to do:
Check if OpenTable has a record for that particular listing that day = 7, if its 7 (which is not a day of the week) then return "Open 24 hours".
If does not exist then return the next record, since SQL Servers datepart(dw.. is 1 based, following will be used select datepart(dw,getdate())-1 to get a 0 day based day of week starting on Sunday (Sunday being 0)
Return nothing if no records exist that match the criteria.
I would appreciate some help on this. I attempted to write this but could not get far. I am not sure how to declare variables for day of the week in the view.
UPDATE
here is my function, anyone see any glaring inefficiencies?
ALTER FUNCTION [dbo].[GetTodaysOpenHours](#ListingID int)
RETURNS VARCHAR(50)
AS
BEGIN
DECLARE #DayOfWeek int
--SQL Day of week starts on sunday but it is 1 based, listing open hours are 0 based
SET #DayOfWeek = DATEPART(DW, GETDATE()) - 1
DECLARE #OpenHours VARCHAR(50)
IF EXISTS(SELECT * FROM OpenHours WHERE Day = 7 AND ListingID = #ListingID)
SET #OpenHours = 'Open 24 Hours'
ELSE
SELECT #OpenHours = Hours FROM OpenHours WHERE ListingID = #ListingID AND Day = #DayOfWeek
RETURN #OpenHours
END
UPDATED VIEW
ALTER view [dbo].[vListings]
as
SELECT l.ListingID, l.ExpiryDate, l.IsApproved, l.IsActive, l.Position,MoneyField1, DateField1,
IntField1, IntField2, IntField3, IntField4,
BoolField1, BoolField2, BoolField3,
OptionField1, OptionField2, OptionField3, OptionField4,
IsTop, TopStartDate, TopExpireDate, Address, Address + ' ' + c.Name + ' ' + p.Name AS FullAddress,
o1.Description as Options1Description,
o2.Description as Options2Description,
o3.Description as Options3Description,
o4.Description as Options4Description,
COALESCE(
(SELECT TOP 1 ThumbnailPath
FROM Attachments
WHERE ListingID = l.listingID), '/content/images/noImageThumbnail2.jpg') AS MainThumbnail,
COALESCE(
(SELECT TOP 1 ThumbnailPath2
FROM Attachments
WHERE ListingID = l.listingID), '/content/images/noImageThumbnail.jpg') AS MainThumbnail2,
l.UserID,
c.SubDomainName as CitySubDomainName, l.Name,
CASE
WHEN l.IsAutoGenerated = 1 THEN l.ImportedPhoneNumber
ELSE dbo.FormatPhoneNumber(u.PhoneNumber)
END as PhoneNumber,
CASE
WHEN l.IsAutoGenerated = 1 THEN l.ImportedContactInfo
ELSE u.FirstName + ' ' + u.LastName
END as ContractInfo,
p.Abbv as StateAbbv,
cn.Code as CountryCode,
l.Comments, l.UniqueID, l.Rating, l.Website,
(select L.ListingID,
isnull(H1.Hours, H2.Hours) as Hours
from Listings L
outer apply (
select Hours FROM OpenHours H WHERE H.Day = 7
AND H.ListingID = L.ListingID
) H1
outer apply (
select Hours FROM OpenHours H WHERE Day = DATEPART(DW, GETDATE()) - 1
AND H.ListingID = L.ListingID
) H2
--dbo.GetTodaysOpenHours(l.ListingID) as TodaysOpenHours
FROM Listings l
INNER JOIN Cities c ON c.CityID = l.CITYID
INNER JOIN Provinces p ON p.ProvinceID = c.ProvinceID
INNER JOIN Countries cn ON cn.CountryID = p.CountryID
INNER JOIN AspNetUsers u ON u.Id = l.UserID
LEFT OUTER JOIN Options1 o1 ON o1.OptionID = l.OptionField1
LEFT OUTER JOIN Options2 o2 ON o2.OptionID = l.OptionField2
LEFT OUTER JOIN Options3 o3 ON o3.OptionID = l.OptionField3
LEFT OUTER JOIN Options4 o4 ON o4.OptionID = l.OptionField4
GO
I get an error that says "incorrect syntax near the keyword FROM
(FROM Listings l)
I upadted the view (added FROM) to the select statements as well
I prefer not to use t he function because I had to add an index to my openhours table on listingid and day in order to make it a little faster but if adding sql into view itself would be better that would be awesome
User defined function isn't usually the best performing solution. You could try just to add that SQL into the view / query by doing something like this (sorry, can't test this, hopefully there's no syntax errors):
select
L.ListingID,
isnull(H1.Hours, H2.Hours) as Hours
from Listing L
outer apply (
select Hours OpenHours H WHERE H.Day = 7
AND H.ListingID = L.ListingID
) H1
outer apply (
select Hours OpenHours H WHERE Day = DATEPART(DW, GETDATE()) - 1
AND H.ListingID = L.ListingID
) H2

Use SQL to count cases in a certain state at a certain time

I need to develop a query that will count the total number of 'open' cases per month.
I have a 'cases' table with an id and a name, and a 'state_changes' table with a datetime column, a caseid column and a state.
How can I calculate the number of cases in each month that have a record with state 'open' in the past, but without a corresponding record with state closed?
I'm using SQL server 2000.
This should get you close (T-SQL):
SELECT
MONTH(s.casedate) m,
YEAR(s.casedate) y,
COUNT(DISTINCT c.caseid) count_cases
FROM
cases c
INNER JOIN state_changes s ON s.caseid = c.caseid
WHERE
s.state = 'open' /* "with state 'open'" */
AND s.casedate < GETDATE() /* "in the past" */
AND NOT EXISTS ( /* "without corresp. record with state 'closed'" */
SELECT 1 FROM state_changes i WHERE i.caseid = s.caseid AND i.state = 'closed'
)
GROUP BY
MONTH(s.casedate),
YEAR(s.casedate)
EDIT: To make a statistic over all twelve months (independent of actual cases existing in these months) you need a small helper table (let's call it month), that contains nothing but one column (let's call that month as well) with numbers from 1 to 12. Then you join against it:
SELECT
m.month,
COUNT(DISTINCT c.caseid) count_cases
FROM
cases c
INNER JOIN state_changes s ON s.caseid = c.caseid
LEFT JOIN month m ON m.month = MONTH(s.casedate)
WHERE
s.state = 'open'
AND YEAR(c.createddate) = YEAR(GETDATE()) /* whatever */
AND NOT EXISTS (
SELECT 1 FROM state_changes i WHERE i.caseid = s.caseid AND i.state = 'closed'
)
GROUP BY
m.month
ORDER BY
m.month
Create a query of the state changes tables for open events and one for close events.
Create a query that does an outer join of the open to the closed on the case ID returning the case ID from both queries
Query the latter query result for rows where the ID from the "close" event query is null
Count the number of rows in the latter query result.
Something very roughly like (off the top of my head, without correction):
SELECT COUNT (T1.CaseID) FROM (SELECT T1.CaseID AS T1_CaseID, T2.CaseID AS T2_CaseID
FROM ((SELECT CaseID FROM state_changes WHERE state = 'open' AND timestamp BETWEEN 1-Jan-09 AND 30-Jan-09) AS T1 OUTER JOIN (SELECT CaseID FROM state_changes WHERE state = 'closed' AND timestamp BETWEEN 1-Jan-09 AND 30-Jan-09) AS T2 ON T1.CaseID = T2.CaseID)) WHERE T2_CaseID = NULL

Resources