How to find if there is a match in a certain time interval between two different date fields SQL? - sql

I have a column in my fact table that defines whether a Supplier is old or new based on the following case-statement:
CASE
WHEN (SUBSTRING([Reg date], 1, 6) = SUBSTRING([Invoice date], 1, 6)
THEN ('New supplier')
ELSE('Old supplier')
END as [Old/New supplier]
So for example, if a Supplier was registered 201910 and Invoice date was 201910 then the Supplier would be considered a 'New supplier' that month. Now I want to calculate the number of Old/New suppliers for each month by doing an distinct count on Supplier no, which is not a problem. The last step is where it gets tricky, now I want to count the number of New/Old suppliers over a 12-month period(if there has been a match on Invoice date and reg date in any of the lagging 12 months). So I create the following mdx expression:
aggregate(parallelperiod([D Time].[Year-Month-Day].[Year],1,[D Time].[Year-Month-Day].currentmember).lead(1) : [D Time].[Year-Month-Day].currentmember ,[Measures].[Supplier No Distinct Count])
The issue I am facing is that it will count Supplier no "1234" twice since it has been both new and old during that time period. What I wish is that, if it finds one match it would be considered a "New" Supplier for that 12- month period.
This is how the result ends up looking but I want it to be zero for "Old" since Reg date and Invoice date matched once during that 12-month period it should be considered new for the whole Rolling 12 month on 201910
Any help, possible approaches or ideas are highly appreciated.
Best regards,
Rubrix

Aggregate first at the supplier level and then at the type level:
select type, count(*)
from (select supplierid,
(case when min(substring(regdate, 1, 6)) = min(substring(invoicedate, 1, 6))
then 'new' else 'old'
end) as type
from t
group by supplierid
) s
group by type;
Note: I assume your date columns are in some obscure string format for your code to work. Otherwise, you should be using appropriate date functions.

SELECT COUNT(*) OVER () AS TotalCount
FROM Facts
WHERE Regdate BETWEEN(olddate, newdate) OR InvoiceDate BETWEEN(olddate, newdate)
GROUP BY
Supplier
The above query will return all the suppliers within that time period and then group them. Thus COUNT(*) will only include unique subscribers.
You might wanna change the WHERE clause because I didn't quite understand how you are getting the 12 month period. Generally if your where clause returns the suppliers within that time period(they don't have to be unique) then the group by and count will handle the rest.

Related

Trying to figure out a solution to this question

Eliminating duplicate answers and mismatch in SQL
So the problem states that I need to find the transactions that happened each day and there is a mismatch between my answer on the correct answer and I don't know why!
this is a Short database description "Recycling firm"
The firm owns several buy-back centers for the collection of recyclable materials. Each of them receives funds to be paid to the recyclables suppliers. Data on funds received are recorded in the table
Income_o(point, date, inc)
The primary key is (point, date), where the point holds the identifier of the buy-back center, and the date corresponds to the calendar date the funds were received. The date column doesn’t include the time part, thus, money (inc) arrives no more than once a day for each center. Information on payments to the recyclables suppliers is held in the table
Outcome_o(point, date, out)
In this table, the primary key (point, date) ensures each buy-back center reports about payments (out) no more than once a day, too.
For the case income and expenditure may occur more than once a day, another database schema with tables having a primary key consisting of the single column code is used:
Income(code, point, date, inc)
Outcome(code, point, date, out)
Here, the date column doesn’t include the time part, either.
and The question is :
Under the assumption that receipts of money (inc) and payouts (out) can be registered any number of times a day for each collection point [i.e. the code column is the primary key], display a table with one corresponding row for each operating date of each collection point.
Result set: point, date, total payout per day (out), total money intake per day (inc).
Missing values are considered to be NULL.
SELECT Income.point, Income."date", SUM("out"), SUM(inc)
FROM Income left JOIN
Outcome ON Income.point = Outcome.point AND
Income."date" = Outcome."date"
GROUP BY Income.point, Income."date"
UNION
SELECT Outcome.point, Outcome."date", SUM("out"), SUM(inc)
FROM Outcome left JOIN
Income ON Income.point = Outcome.point AND
Income."date" = Outcome."date"
GROUP BY Outcome.point, Outcome."date";
My guess is that you have a bit of a Cartesian join by not including CODE as part of your join criteria. I think the following query should suit your needs:
WITH calendar AS
(
SELECT TRUNC(SYSDATE)-(LEVEL-1) AS DT
FROM DUAL
CONNECT BY LEVEL < 30
)
SELECT d.pnt AS "POINT",
c.dt AS "DATE",
d.outcome_total,
d.income_total
FROM calendar c
LEFT JOIN (SELECT nvl(inc.pnt, outc.pnt) AS PNT,
nvl(inc.dt, outc.dt) AS DT,
outc.amt AS OUTCOME_TOTAL,
inc.amt AS INCOME_TOTAL
FROM (SELECT i.pnt, i.dt, sum(i.inc) AS AMT
FROM income i
GROUP BY i.pnt, i.dt) inc
FULL JOIN (SELECT o.pnt, o.dt, sum(o.inc) AS AMT
FROM outcome o
GROUP BY o.pnt, o.dt) outc ON inc.pnt = outc.pnt AND inc.dt = outc.dt) d ON c.dt = d.dt;
I added the calendar table to account for the case where there was neither an income nor an outcome on a given day. However, if you don't need that, the query within the LEFT JOIN should be just fine.
N.B.: With the addition of the calendar WITH clause, this query will currently only show results from the last month(-ish). If you need longer, adjust the 30 day window.

How to add custom YoY field to output?

I'm attempting to determine the YoY growth by month, 2017 to 2018, for number of Company bookings per property.
I've tried casting and windowed functions but am not obtaining the correct result.
Example Table 1: Bookings
BookID Amnt BookType InDate OutDate PropertyID Name Status
-----------------------------------------------------------------
789555 $1000 Company 1/1/2018 3/1/2018 22111 Wendy Active
478141 $1250 Owner 1/1/2017 2/1/2017 35825 John Cancelled
There are only two book types (e.g., Company, Owner) and two Book Status (e.g., Active and Cancelled).
Example Table 2: Properties
Property ID State Property Start Date Property End Date
---------------------------------------------------------------------
33111 New York 2/3/2017
35825 Michigan 7/21/2016
The Property End Date is blank when the company still owns it.
Example Table 3: Months
Start of Month End of Month
-------------------------------------------
1/1/2018 1/31/2018
The previous developer created this table which includes a row for each month from 2015-2020.
I've tried many various iterations of my current code and can't even come close.
Desired Outcome
I need to find the YoY growth by month, 2017 to 2018, for number of Company bookings per property. The stakeholder has requested the output to have the below columns:
Month Name Bookings_Per_Property_2017 Bookings_Per_Property_2018 YoY
-----------------------------------------------------------------------
The number of Company bookings per property in a month should be calculated by counting the total number of active Company bookings made in a month divided by the total number of properties active in the month.
Here is a solution that should be close to what you need. It works by:
LEFT JOINing the three tables; the important part is to properly check the overlaps in date ranges between months(StartOfMonth, EndOfMonth), bookings(InDate, OutDate) and properties(PropertyStartDate, PropertyEndDate): you can have a look at this reference post for general discussion on how to proceed efficiently
aggregating by month, and using conditional COUNT(DISTINCT ...) to count the number of properties and bookings in each month and year. The logic implicitly relies on the fact that this aggregate function ignores NULL values. Since we are using LEFT JOINs, we also need to handle the possibility that a denominator could have a 0 value.
Notes:
you did not provide expected results so this cannot be tested
also, you did not explain how to compute the YoY column, so I left it alone; I assume that you can easily compute it from the other columns
Query:
SELECT
MONTH(m.StartOfMonth) AS [Month],
COUNT(DISTINCT CASE WHEN YEAR(StartOfMonth) = 2017 THEN b.BookID END)
/ NULLIF(COUNT(DISTINCT CASE WHEN YEAR(StartOfMonth) = 2017 THEN p.PropertyID END), 0)
AS Bookings_Per_Property_2017,
COUNT(DISTINCT CASE WHEN YEAR(StartOfMonth) = 2018 THEN b.BookID END)
/ NULLIF(COUNT(DISTINCT CASE WHEN YEAR(StartOfMonth) = 2018 THEN p.PropertyID END), 0)
AS Bookings_Per_Property_2018
FROM months m
LEFT JOIN bookings b
ON m.StartOfMonth <= b.OutDate
AND m.EndOfMonth >= b.InDate
AND b.status = 'Active'
AND b.BookType = 'Company'
LEFT JOIN properties p
ON m.StartOfMonth <= COLAESCE(p.PropertyEndDate, m.StartOfMonth)
AND m.EndOfMonth >= p.PropertyStartDate
GROUP BY MONTH(m.StartOfMonth)

Subtracting results from two queries / sub queries

I'm trying to create a query which returns the results of customer orders (created in a month e.g. January) - the cancelled customer orders in that exact month (cancelled customer orders in month of January) and display the results grouped by location (Rows) and by year with month (Columns).
Currently I have a table containing all the customer order information both created and cancelled. Each customer order has a unique order number, location where it was sold, creation date and cancellation date. If the customer order is still valid, then the cancellation date will be null or "//". If the customer order is cancelled then it will have a cancellation date. As some additional information a customer order can be created in January 2019 and cancelled in July or August, or December etc. What I would like to obtain is the net customer orders for all the months by doing gross customer orders for a month - cancelled customer orders for that month and for a specific location = net customer orders for that month for that location.
In order to achieve this what I have tried, was to create two separate queries from the table, first one containing all the valid customer orders and the second one containing all the cancellations. Then i tried creating a cross-tab between the two other queries, trying to count what I mentioned above, grouping by location and then pivoting the of the year and month.
First query with valid customer orders named cust_valid (simplified):
SELECT cust_ords.[SaleLoc], cust_ords.[OrderNum], cust_ords.[CreationDate], cust_ords.[CancelDate]
FROM cust_ords
WHERE cust_ords.[CancelDate] = "" OR cust_ords.[CancelDate] = "//";
Second query with cancelled customer orders named cust_cancelled (simplified):
SELECT cust_ords.[SaleLoc], cust_ords.[OrderNum], cust_ords.[CreationDate], cust_ords.[CancelDate]
FROM cust_ords
WHERE cust_ords.[CancelDate] <> "" OR cust_ords.[CancelDate] <> "//";
Last, a cross-tab between them:
TRANSFORM Count(cust_valid.[OrderNum]) AS [NetOrderCount]
SELECT cust_valid.[SaleLoc]
FROM cust_valid LEFT JOIN cust_cancelled ON cust_valid.[CreationDate] = cust_cancelled.[CancelDate]
WHERE cust_valid.[CreationDate] = cust_cancelled.[CancelDate]
GROUP BY cust_ords.[SaleLoc]
PIVOT cust_valid.[CreationDate];
In this sense, I am trying to obtain (count) the net customer orders (total created for a month - what was cancelled in that month) for every given location and display the results per month (basically the columns names should be the year and the month). So for example if i have 10 customer orders in January, 5 in February and 15 in March, if 3 of the ones in January get cancelled in March, then I would like to count for the month of March 15 - 3, thus ending up with January 10, February 5, March 12.
First of all, you say an order is valid is valid if cancellation date is null or //, however you test for:
WHERE cust_ords.[CancelDate] = "" OR cust_ords.[CancelDate] = "//";
To test for null use [CancelDate] is null, or shorthand the test to ISNULL([CancelDate],'//')='//'
Second, in your second query you test for cancelled orders, with
WHERE cust_ords.[CancelDate] <> "" OR cust_ords.[CancelDate] <> "//";
That is not the negation of your test for cancelled orders!
!(A or B) => !A and !B
So you should use
WHERE cust_ords.[CancelDate] <> "" and cust_ords.[CancelDate] <> "//";
Or rather ISNULL(cust_ords.[CancelDate],'//')!='//'
Noow to your query itself, you are joining on dates, that is you join orders on a given date with cancellations on the same date. However you want to see orders and cancellation pr month. Since you left join cancellations you will only ever count cancellations that happen on the same date as orders!
SELECT
cust_valid.[SaleLoc]
, Format(
iif(isnull(cust_ords.[CancelDate],'//')='//'
,cust_ords.[CreationDate]
,cust_ords.[CancelDate]
,'MMMM yy') Mnth
, sum(iif(isnull(cust_ords.[CancelDate],'//'='//',1,0)) ValidOrders
, sum(iif(isnull(cust_ords.[CancelDate],'//'='//',0,1)) CancelledOrders
, sum(iif(isnull(cust_ords.[CancelDate],'//'='//',1,0))
- sum(iif(isnull(cust_ords.[CancelDate],'//'='//',0,1)) NetOrderCount
FROM
cust_ords
group by
cust_valid.[SaleLoc]
, Format(
iif(isnull(cust_ords.[CancelDate],'//')='//'
,cust_ords.[CreationDate]
,cust_ords.[CancelDate]
,'MMMM yy')
This should give you the basic data useful for pivoting, at least in SQL Server
THank you #Søren Kongstad for your useful explanations. I have modified / corrected the code accordingly to provide me with the needed results:
SELECT CustOrders.[Grupp namn], Format(IIf(CustOrders.[DatAnnulCde]="/ /",CustOrders.[DatCreatCde],CustOrders.[DatAnnulCde]),"yyyy-mm") AS year_month,
Sum(IIf(IsNull(CustOrders.DatCreatCde),0,1)) AS GrossOrders, Sum(IIf(CustOrders.DatAnnulCde<>"/ /",1,0)) AS CancelledOrders,
Sum(IIf(IsNull(CustOrders.DatCreatCde),0,1)) - Sum(IIf(CustOrders.DatAnnulCde<>"/ /",1,0)) AS NetOrders
FROM CustOrders
WHERE CustOrders.[CarType] = "Renault PC" And CustOrders.[DatCreatCde] >= Format(Year(Now())&"-01-01","yyyy-mm-dd")
GROUP BY CustOrders.[Grupp namn], Format(IIf(CustOrders.[DatAnnulCde]="/ /",CustOrders.[DatCreatCde],CustOrders.[DatAnnulCde]),"yyyy-mm");
The fields have different names then in the initial examples
DatAnnulCde = CancelDate
DatCreatCde = CreationDate

SSRS Report Multi Parameters (start date,end date, MeterId, Displayby)

In my SSRS report there are 4 parameters StartDate, EndDate, MeterId, & DisplayBy
Start Date: datetime datatype
EndDate : datetime datatype
MeterId : is a drop down list and this will populate based on SQL query
DisplayBy: is a drop down list and this has the following values (Hour,day,Month & year)
The Database that stores hourly values for Meters, the following are the DB table columns: (MeterId,ReadingDate,Hours,Quantity,Price)
When I select the startdate, end date and the meter Id and display i want to show report based on the startdate & enddate and then by display values.
If the display is hour, the we got display all the 24 hour values for the MeterId,Quantity, Price for the date range.
If the display is day, we got display total quantity and total price for the MeterId for that date range.
If the display is Month, we got display total quantity and total price for the MeterId for that date range.
If the display is Year, we got display total quantity and total price for the MeterId for that date range. Say for example If i select start date as 1-1-2016 and end date as 12-31-2016. My result should show 12 rows for each month with their total Quantity, Total price for that particular MeterID.
my DB table stores all the hourly values i know how to show the values on screen if the user selects the display dropdown as hour. But, dont know how to show the result for day/month/year or how to group it. Do I need to use "case" statement and if so what should i need to give on display parameters.
Please suggest your idea...
Row Grouping:
SELECT I.CustomerName, I.ReadingDate, I.IntegratedHour, I.IntegratedUsage, I.IntegratedGeneration, DATEPART(dd, I.ReadingDate) AS [Reading Day], DATEPART(mm,
I.ReadingDate) AS [Reading Month], DATEPART(yyyy, I.ReadingDate) AS [Reading Year]
FROM IntegratedHour_MV90 AS I INNER JOIN
CustRptMeterExtract AS CT ON CT.CustomerName = I.CustomerName
WHERE (I.ReadingDate >= #StartDate) AND (I.ReadingDate <= #EndDate) AND (I.CustomerName IN (#FacilityName))
Expected Result:
SSRS Current Output: Doesnot match
Depending on your layout you could set row grouping to an expression something like this
=SWITCH
(
Parameters!ReportBy.Value=1, Fields!Hour.Value,
Parameters!ReportBy.Value=2, Fields!Day.Value,
Parameters!ReportBy.Value=3, Fields!Month.Value,
Parameters!ReportBy.Value=4, Fields!Year.Value,
True, 0)
This assumes you have already have the hours/days/months/years in your dataset, if not then you would have to replace the field references with expressions to return the relevant month etc.
Based on what I can see above you'll need to add a grouping level for Customer before the group expression. Also, you Quantity expression should be a sum something like this
=SUM(FIelds!IntegratedGeneration.Value)
You may still have a problem though. I'm assuming Price is a unit price, so it does not make sense to sum that too. To get round this, you should calculate the LineValue (Qty * Price) in your dataset then change the price expression to be something like
=(SUM(FIelds!LineValue.Value)/SUM(Fields!IntegratedGeneratio‌​n.Value))
and this will give you the average price.
However, this may be slow and personally I would do the work in your dataset. Again assuming you have the months, years in your table then you could do something like this.
--DECLARE #ReportBy int = 1 -- uncomment for testing
select
MeterID, Price
, CASE #ReportBy
WHEN 1 THEN [Month]
WHEN 2 THEN [Year]
ELSE NULL
END AS GroupByColumn
INTO #t
from dbo.MyDataTable
SELECT
GroupByColumn
, SUM(Price) as Price
FROM #t
Group BY GroupByColumn
Order by GroupByColumn
This assumes your report parameter is called ReportBy, if not just swap the name out.

How to create a dynamic where clause in sql?

So I have created a table that has the following columns from a transaction table with all customer purchase records:
1. Month-Year, 2.Customer ID, 3. Number of Transactions in that month.
I'm trying to create a table that has the output of
1. Month-Year, 2. Number of active customers defined by having at least 1 purchase in the previous year.
The code that I have currently is this but the case when obviously only capturing one date and the where clause isn't dynamic. Would really appreciate your help.
select month_start_date, cust_ID,
(case when month_start_Date between date and add_months(date, -12) then count(cust_ID) else 0 end) as active
from myserver.mytable
where
month_start_Date>add_months(month_start_date,-12)
group by 1,2
EDIT: I'm just trying to put a flag next to a customer if they are active in each month defined as having at least one transaction in the last year thanks!
You might use Teradata's proprietary EXPAND ON synax for creating time series:
SELECT month_start_date, COUNT(*)
FROM
( -- create one row for every month within the next year
-- after a customer's transaction
SELECT DISTINCT
BEGIN(pd) AS month_start_date,
cust_ID
FROM myserver.mytable
EXPAND ON PERIOD(month_start_date, ADD_MONTHS(month_start_date,12)) AS pd
BY ANCHOR MONTH_BEGIN -- every 1st of month
FOR PERIOD (DATE - 500, DATE) -- use this to restrict to a specific date range
) AS dt
GROUP BY month_start_date
ORDER BY month_start_date