Access Crosstab Query: based on sales totals within TWO date ranges - sql

I'm looking for a way to create an Access crosstab query reporting sales totals by 'Brand', for two different date ranges.:
For Distributor: "DistID" (column, not visible)
Sales Totals: "Sales" (column)
TWO different date ranges: "depDate" for Period 1 and Period 2 (column):
Period1 = Between [forms]![frmRPT_YTDDepl_SF]![BDT1] And [forms]![frmRPT_YTDDepl_SF]![EDT1]
Period2 = Between [forms]![frmRPT_YTDDepl_SF]![BDT2] And [forms]![frmRPT_YTDDepl_SF]![EDT2]
Brands = "DprodBrand" (rows)
Currently, I have TWO separate crosstab queries for each period, working properly. -> CODE BELOW.
I am looking for a way to create ONE query displaying Brand's sales totals for each date range, in two separate columns or one crosstab query.
Period1:
TRANSFORM Sum(tblDepletions_DETAIL.detQuan) AS Sales
SELECT tblProducts_DEPL.DprodBrand
FROM tblDepletions INNER JOIN (tblProducts_DEPL INNER JOIN tblDepletions_DETAIL ON tblProducts_DEPL.DprodZSKU = tblDepletions_DETAIL.detZSKU) ON tblDepletions.depID = tblDepletions_DETAIL.detDeplID
WHERE (((tblDepletions.depDate) Between [forms]![frmRPT_YTDDepl_SF]![BDT1] And [forms]![frmRPT_YTDDepl_SF]![EDT1]) AND ((tblDepletions.depDistID)=132))
GROUP BY tblProducts_DEPL.DprodBrand
ORDER BY tblProducts_DEPL.DprodBrand
PIVOT Format([depDate],"yy")-(Format(Date(),"yy"))+2 In (1,2);
Period2:
TRANSFORM Sum(tblDepletions_DETAIL.detQuan) AS Sales
SELECT tblProducts_DEPL.DprodBrand
FROM tblDepletions INNER JOIN (tblProducts_DEPL INNER JOIN tblDepletions_DETAIL ON tblProducts_DEPL.DprodZSKU = tblDepletions_DETAIL.detZSKU) ON tblDepletions.depID = tblDepletions_DETAIL.detDeplID
WHERE (((tblDepletions.depDate) Between [forms]![frmRPT_YTDDepl_SF]![BDT2] And [forms]![frmRPT_YTDDepl_SF]![EDT2]) AND ((tblDepletions.depDistID)=132))
GROUP BY tblProducts_DEPL.DprodBrand
ORDER BY tblProducts_DEPL.DprodBrand
PIVOT Format([depDate],"yy")-(Format(Date(),"yy"))+2 In (1,2);
Many Thanks!!! ~~ Jacob

Consider simply joining the two saved, crosstab queries like any other pair of queries or tables using the DprodBrand as join key:
SELECT CrosstabQ1.DprodBrand,
CrosstabQ1.[1] As Period1_Year1, CrosstabQ2.[1] As Period2_Year1,
CrosstabQ1.[2] As Period1_Year2, CrosstabQ2.[2] As Period2_Year2
FROM CrosstabQ1
INNER JOIN CrosstabQ2 ON CrosstabQ1.DprodBrand = CrosstabQ2.DprodBrand
Now if you only want one query to do it all, consider the conditional aggregate pivot query since crosstabs cannot be used as subqueries. Here you migrate WHERE to IIF() conditions:
SELECT p.DprodBrand,
SUM(IIF((d.depDate BETWEEN [Forms]![frmRPT_YTDDepl_SF]![BDT1]
AND [Forms]![frmRPT_YTDDepl_SF]![EDT1])
AND (Format(d.[depDate],"yy")-(Format(Date(),"yy"))+2 = 1),
dt.detQuan, NULL)) AS Period1_Year1,
SUM(IIF((d.depDate BETWEEN [Forms]![frmRPT_YTDDepl_SF]![BDT2]
AND [Forms]![frmRPT_YTDDepl_SF]![EDT2)
AND (Format(d.[depDate],"yy")-(Format(Date(),"yy"))+2 = 1),
dt.detQuan, NULL)) AS Period2_Year1,
SUM(IIF((d.depDate BETWEEN [Forms]![frmRPT_YTDDepl_SF]![BDT1]
AND [Forms]![frmRPT_YTDDepl_SF]![EDT1])
AND (Format(d.[depDate],"yy")-(Format(Date(),"yy"))+2 = 2),
dt.detQuan, NULL)) AS Period1_Year2,
SUM(IIF((d.depDate BETWEEN [Forms]![frmRPT_YTDDepl_SF]![BDT2]
AND [Forms]![frmRPT_YTDDepl_SF]![EDT2])
AND (Format(d.[depDate],"yy")-(Format(Date(),"yy"))+2 = 2),
dt.detQuan, NULL)) AS Period2_Year2
FROM tblDepletions d
INNER JOIN (tblProducts_DEPL p
INNER JOIN tblDepletions_DETAIL dt
ON p.DprodZSKU = dt.detZSKU)
ON d.depID = dt.detDeplID
WHERE ((d.depDistID)=132)
GROUP BY p.DprodBrand
ORDER BY p.DprodBrand

As this is Access, it might be simpler to save the two queries leaving out the ORDER BY.
Then create a new query:
SELECT *
FROM Q1
UNION ALL
SELECT *
FROM Q2
ORDER BY DprodBrand

By: Dale Fye (Access MVP):
I'm not sure you need a CrossTab for this.
Select DProdBrand,
SUM(IIF([DepDate] BETWEEN [Forms]![frmRpt_YTDDepl_SF]![BDT1]
AND [[forms]![frmRPT_YTDDepl_SF]![EDT1], [Sales], 0) as Period1,
SUM(IIF([DepDate] Between [forms]![frmRPT_YTDDepl_SF]![BDT2]
AND [forms]![frmRPT_YTDDepl_SF]![EDT2], [Sales], 0) as Period2,
SUM([Sales]) as [Sales Total]
FROM yourTable
GROUP BY DProdBrand
https://www.experts-exchange.com/questions/28978325/Access-Crosstab-Query-based-on-sales-totals-within-TWO-date-ranges.html

Related

SQL Queries Returning Non-equivalent Results and Different Counts Every Run

So I have 2 SQL queries both including the same variable: basically (n_orders and orders_count) should return the same exact results. Problem is:
the 2 columns are not always equivalent for all values
the count of different values changes every run
so first run could be that 20 rows have different (n_orders, orders_count) values then 2nd run says count of different values is 56 for example and so on with changing counts every run.
Query 1:
SELECT product_id,
packing_unit_id,
count(DISTINCT product_sales_order.sales_order_id)
FROM product_sales_order
WHERE product_sales_order.created_at::date BETWEEN '{start}' AND '{end}'
GROUP BY 1, 2
ORDER BY product_id, packing_unit_id
Query 2:
select kpis.*, lr.lr
FROM
(SELECT product_sales_order.product_id,
product_sales_order.packing_unit_id,
count(DISTINCT product_sales_order.sales_order_id) AS orders_count,
count(DISTINCT sales_orders.retailer_id) AS retailers_count,
count(DISTINCT product_sales_order.sales_order_id)*1.0 / count(DISTINCT sales_orders.retailer_id) AS frequency,
(count(DISTINCT sales_orders.retailer_id)*1.0 /(SELECT count(DISTINCT sales_orders.retailer_id) AS month_retailers
FROM sales_orders
JOIN retailers on retailers.id = sales_orders.retailer_id
WHERE sales_orders.created_at::Date BETWEEN '{start}' AND '{end}'
AND sales_orders.sales_order_status_id = 6
AND retailers.is_market_type_private = false)) AS reach,
sum(product_sales_order.total_price) AS nmv,
(sum(product_sales_order.total_price)*1.0 / (SELECT sum(product_sales_order.total_price) AS month_nmv
FROM product_sales_order
WHERE product_sales_order.created_at::Date BETWEEN '{start}' AND '{end}'
AND product_sales_order.purchased_item_count <> 0)) AS contribution,
sum(product_sales_order.purchased_item_count * product_sales_order.basic_unit_count) AS bskt_size,
sum(product_sales_order.total_price)*1.0 / count(DISTINCT product_sales_order.sales_order_id) AS avg_ts,
sum(product_sales_order.total_price)*1.0 / count(DISTINCT sales_orders.retailer_id) AS nmv_p_retailer
FROM product_sales_order
LEFT JOIN sales_orders ON sales_orders.id = product_sales_order.sales_order_id
LEFT JOIN products ON products.id = product_sales_order.product_id
LEFT JOIN retailers on retailers.id = sales_orders.retailer_id
WHERE product_sales_order.created_at::date BETWEEN '{start}' AND '{end}'
GROUP BY 1,2
ORDER BY product_sales_order.product_id, product_sales_order.packing_unit_id, orders_count
) as kpis
LEFT JOIN (
SELECT performance.lost_revenue.product_id,
sum(performance.lost_revenue.lost_revenue) as lr
FROM performance.lost_revenue
WHERE performance.lost_revenue.created_at::Date between '{start}' AND '{end}'
GROUP BY 1
)as lr on lr.product_id = kpis.product_id
What could be corrected regarding the structure of the 2nd SQL query to make it yield the same results for orders_count?
Why does different values count return different results every run?

Error getting the amount grouped by name and account number SQL Server

I have an issue on my SQL query. I tried doing two ways
With the first query I got the right amount but I lose some name descriptions
With the second I got every name descriptions but I got a lower amount.
Context: I want to get the revenue gotten between two dates.
I need the following columns from tables
Table reciboDet I need the columns CtaIngreso, ValorUnitReciboDet
Table CuentaIngreso_A the column nombrectaingreso, ctaingreso (only to create the join)
Table Recibo the columns FechaRecibo and ReciboAnulado
To get the right name descriptions I need to verify the receipt year that was in the table AvpgEnc, but when I do that a lose the amount.
First query
SELECT
ReciboDet.CtaIngreso
, SUM(ReciboDet.ValorUnitReciboDet) AS Total
, CuentaIngreso_A.NombreCtaIngreso
FROM
ReciboDet
INNER JOIN CuentaIngreso_A
ON ReciboDet.CtaIngreso = CuentaIngreso_A.CtaIngreso
WHERE
(ReciboDet.NumRecibo IN
(SELECT NumRecibo
FROM Recibo
WHERE (FechaRecibo BETWEEN '01/10/2020' AND '31/10/2020')
AND (ReciboAnulado = 0)
AND (CuentaIngreso_A.Anio = DATEPART(year, FechaRecibo))
)
)
GROUP BY
ReciboDet.CtaIngreso
, CuentaIngreso_A.NombreCtaIngreso
ORDER BY
CuentaIngreso_A.NombreCtaIngreso
Second query
SELECT
ReciboDet.CtaIngreso [cuenta],
sum(ReciboDet.ValorUnitReciboDet) [monto],
CuentaIngreso_A.NombreCtaIngreso [descripcion]
FROM
ReciboDet
inner join avpgenc
on ReciboDet.NumFactura = AvPgEnc.NumAvPg
inner join CuentaIngreso_A
on ReciboDet.CtaIngreso = CuentaIngreso_A.CtaIngreso
WHERE
(ReciboDet.NumRecibo IN
(SELECT NumRecibo
FROM Recibo
WHERE (FechaRecibo BETWEEN '01/10/2020' AND '31/10/2020')
AND (ReciboAnulado = 0)
)
AND (year(AvPgEnc.FechaVenceAvPg) = CuentaIngreso_A.Anio)
)
GROUP BY
ReciboDet.CtaIngreso
, CuentaIngreso_A.NombreCtaIngreso
ORDER BY
ReciboDet.CtaIngreso

How to force postgres to return 0 even if there are no rows matching query, using coalesce, group by and join

I've been trying hopelessly to get the following SQL statement to return the query results and default to 0 if there are no rows matching the query.
This is the intended result:
vol | year
-------+------
0 | 2018
Instead I get:
vol | year
-----+------
(0 rows)
Here is the sql statement:
select coalesce(vol,0) as vol, year
from (select sum(vol) as vol, year
from schema.fact_data
join schema.period_data
on schema.fact_data.period_tag = schema.period_data.tag
join schema.product_data
on schema.fact_data.product_tag =
schema.product_data.tag
join schema.market_data
on schema.fact_data.market_tag = schema.market_data.tag
where "retailer"='MadeUpRetailer'
and "product_tag"='FakeProductTag'
and "year"='2018' group by year
) as DerivedTable;
I know the query works because it returns data when there is data. Just doesn't default to 0 as intended...
Any help in finding why this is the case would be much appreciated!
Using your subquery DerivedTable, you could write:
SELECT coalesce(DerivedTable.vol, 0) AS vol,
y.year
FROM (VALUES ('2018'::text)) AS y(year)
LEFT JOIN (SELECT ...) AS DerivedTable
ON DerivedTable.year = y.year;
Remove the GROUP BY (and the outer query):
select 2018 as year, coalesce(sum(vol), 0) as vol
from schema.fact_data f join
schema.period_data p
on f.period_tag = p.tag join
schema.product_data pr
on f.product_tag = pr.tag join
schema.market_data m
on fd.market_tag = m.tag
where "retailer" = 'MadeUpRetailer' and
"product_tag" = 'FakeProductTag' and
"year" = '2018';
An aggregation query with no GROUP BY always returns exactly one row, so this should do what you want.
EDIT:
The query would look something like this:
select v.yyyy as year, coalesce(sum(vol), 0) as vol
from (values (2018), (2019)) v(yyyy) left join
schema.fact_data f
on f.year = v.yyyy left join -- this is just an example. I have no idea where year is coming from
schema.period_data p
on f.period_tag = p.tag left join
schema.product_data pr
on f.product_tag = pr.tag left join
schema.market_data m
on fd.market_tag = m.tag
group by v.yyyy
However, you have to move the where conditions to the appropriate on clauses. I have no idea where the columns are coming from.
From the code you posted it is not clear in which table you have the year column.
You can use UNION to fetch just 1 row in case there are no rows in that table for the year 2018 like this:
select sum(vol) as vol, year
from schema.fact_data innrt join schema.period_data
on schema.fact_data.period_tag = schema.period_data.tag
inner join schema.product_data
on schema.fact_data.product_tag = schema.product_data.tag
inner join schema.market_data
on schema.fact_data.market_tag = schema.market_data.tag
where
"retailer"='MadeUpRetailer' and
"product_tag"='FakeProductTag' and
"year"='2018'
group by "year"
union
select 0 as vol, '2018' as year
where not exists (
select 1 from tablename where "year" = '2018'
)
In case there are rows for the year 2018, then nothing will be fetched by the 2nd query,

Filter with Dates but keep the calculation

I would like to know what can you do in the following scenario:
Lets say I am filtering on a date in the where clause (between eomonth(#StartDate) and eomonth(getdate()-1). I have a calculated column that is correct when I run the query without any filter, but the problem is that when I filter lets say #StartDate = 06/30/2017 then the calculations will obviously change. Is there any way of doing this?
The calculated column is a windowing function.
Edited:
I have added a picture of the data. So I am using a windowing function to calculate the agentfourmonthperiod. You will see that it sums the units for that month and the 3 previous months. This I dont want to change when filtering. So the units and agentfourmonthperiod columns should stay exactly the same after filtering on the #StartDate. Please see data below:
I want to design an SSRS report that the user will be filtering on the #StartDate, but then it should show the calculation as it does when no filter in used.
Hope this makes sense. Otherwise I can add the code. Its just quite long though.
Code:
WITH DATA
AS
(
SELECT
EOMONTH(SubmissionDates.original_date_c) AS IntakeMonth,
ProvincialArea.SAD_ProvMananger AS ProvManager,
RegionalArea.SAD_RegMananger AS RegManager,
SalesArea.SAD_SalesManager AS AreaSalesManager,
ConsultantUserExt.name AS Consultant,
COUNT(LeadsLink.LeadsID) OVER(PARTITION BY ConsultantUserExt.name, EOMONTH(SubmissionDates.original_date_c,0) ORDER BY EOMONTH(SubmissionDates.original_date_c,0)) AS Unit,
ROW_NUMBER() OVER(PARTITION BY ConsultantUserExt.name, EOMONTH(SubmissionDates.original_date_c,0) ORDER BY EOMONTH(SubmissionDates.original_date_c,0)) AS rn
FROM Import.OobaApplication as Application
LEFT OUTER JOIN Import.OobaApplicant applicant ON application.ApplicationID = applicant.ApplicationID
AND applicant.PrincipleApplication = 'Y'
LEFT OUTER JOIN usr_userext_cstm ON Application.Consultant = usr_userext_cstm.comcorp_key_c
or Application.Consultant = usr_userext_cstm.deal_maker_key_c
or Application.Consultant = usr_userext_cstm.ops_key_c
LEFT OUTER JOIN usr_userext AS ConsultantUserExt ON usr_userext_cstm.id_c = ConsultantUserExt.id AND ConsultantUserExt.deleted = 0
LEFT OUTER JOIN usr_userext_cstm AS ConsultantUserExtCstm on ConsultantUserExt.id = ConsultantUserExtCstm.id_c
LEFT OUTER JOIN CapcubedInternalDB.dbo.ProvincialArea AS ProvincialArea ON ConsultantUserExtCstm.sad_provincialmanager_c = ProvincialArea.ID
LEFT OUTER JOIN CapcubedInternalDB.dbo.RegionalArea AS RegionalArea ON ConsultantUserExtCstm.sad_regionalmanager_c = RegionalArea.ID
LEFT OUTER JOIN CapcubedInternalDB.dbo.SalesArea AS SalesArea ON ConsultantUserExtCstm.sad_salesmanager_c = SalesArea.ID
LEFT OUTER JOIN CapcubedInternalDB.dbo.LeadsLink AS LeadsLink ON Application.ApplicationID = LeadsLink.GroupCode
LEFT OUTER JOIN suitecrmprod.dbo.leads AS SuiteLeads ON LeadsLink.LeadsID = SuiteLeads.ID
--Latest Bank Submission
LEFT OUTER JOIN (SELECT
bankSub.ApplicationID As BankSubAppID, bankSub.SubmissionDate,
bankSub.Bank, bankSub.RequiredLoanAmount,
bankSub.BankCode AS BankSubBankCode
FROM Import.OobaBankSubmission bankSub
LEFT OUTER JOIN Import.OobaBankSubmission later ON bankSub.ApplicationID = later.ApplicationID
AND bankSub.SubmissionDate > later.SubmissionDate
WHERE later.applicationID IS NULL) AS BankSub ON Application.ApplicationID = BankSub.BankSubAppID
LEFT OUTER JOIN ccrep_calendar_cstm AS SubmissionDates ON CONVERT(VARCHAR(10),BankSub.SubmissionDate,101) = SubmissionDates.original_date_c
WHERE SubmissionDates.cc_date_c BETWEEN COALESCE(EOMONTH(#StartDate), '01/31/2016') AND COALESCE(#EndDate, GETDATE(), -1)
AND ConsultantUserExtCstm.consultantstatus_c NOT LIKE 2
)
SELECT *
INTO #Rn
FROM DATA
WHERE rn = 1
SELECT i.IntakeMonth, c.ProvManager, c.RegManager, c.AreaSalesManager, c.Consultant, COALESCE(#Rn.Unit, 0) AS Unit
INTO #FillData
FROM (SELECT DISTINCT IntakeMonth FROM #Rn) AS i
CROSS JOIN
(SELECT DISTINCT Consultant, ProvManager, RegManager, AreaSalesManager FROM #Rn) AS c
LEFT OUTER JOIN #Rn ON #Rn.IntakeMonth = i.IntakeMonth AND #Rn.Consultant = c.Consultant
ORDER BY Consultant, IntakeMonth
SELECT
IntakeMonth,
Consultant,
Unit,
SUM(Unit) OVER(PARTITION BY Consultant ORDER BY Consultant ROWS BETWEEN 3 PRECEDING AND CURRENT ROW) AS agentfourmonthperiod
FROM #FillData
WHERE ('(All)' IN (#ProvincialManager) OR (ProvManager IN (#ProvincialManager)))
AND ('(All)' IN (#RegionalManager) OR (RegManager IN (#RegionalManager)))
AND ('(All)' IN (#AreaSalesManager) OR (AreaSalesManager IN (#AreaSalesManager)))
AND ('(All)' IN (#Consultant) OR (Consultant IN (#Consultant)))
DROP TABLE #Rn
DROP TABLE #FillData
You could of course remove any filter on dates from the query and apply them directly in the tablix of your report. Obvously, this means that SQL Server has to return all the data each time the report is run, so I guess that this isn't what you want.
For the window function to have access to the previous 3 rows, you will have to include the previous 3 months in your calculation. To achieve this, change the first condition in the WHERE clause in the cte (data) to something like this:
SubmissionDates.cc_date_c
BETWEEN
ISNULL(DATEADD(month, DATEDIFF(month, 0, #StartDate)-3, 0), '01/10/2015')
AND
ISNULL(#EndDate, DATEADD(day, DATEDIFF(day, 0, GETDATE())-1, 0))
As I thought that your date filter logic was wrong, I changed it to include the dates from the beginning of the month rather than from the end.
Now that the previous 3 months are included, we can apply a filter in the end to exclude the previous months from display, but this has to be done after using the window function, for example with another cte:
WITH calc AS (
SELECT
IntakeMonth,
Consultant,
Unit,
SUM(Unit) OVER(PARTITION BY Consultant ORDER BY Consultant ROWS BETWEEN 3 PRECEDING AND CURRENT ROW) AS agentfourmonthperiod
FROM #FillData
WHERE ('(All)' IN (#ProvincialManager) OR (ProvManager IN (#ProvincialManager)))
AND ('(All)' IN (#RegionalManager) OR (RegManager IN (#RegionalManager)))
AND ('(All)' IN (#AreaSalesManager) OR (AreaSalesManager IN (#AreaSalesManager)))
AND ('(All)' IN (#Consultant) OR (Consultant IN (#Consultant)))
)
SELECT IntakeMonth, Consultant, Unit, agentfourmonthperiod
FROM calc
WHERE IntakeMonth >= ISNULL(EOMONTH(#StartDate), '01/31/2016')

SQL Query returning multiple Duplicate Results

scenario : I have Three Tables(Prisoners,AddPaymentTransaction,WithdrawPaymentTransation)
Date in Tables : i have 1 row of prisoner with PrisonerID=5 and two rows in both other table,
i have wrote query to return there data if any prisoner have add some payment in there account or with draw any payment from there payment on same day or on different dates etc.
here is my query :
select at.PrisonerID ,at.Amount as AAmount,at.Date as ADate,wt.Amount as WAmount,wt.Date as WDate
from Prisoners p, AddPaymentTransaction at,WithdrawPaymentTransation wt
where p.PrisonerID=at.PrisonerID and p.PrisonerID=wt.PrisonerID and at.PrisonerID=wt.PrisonerID and at.PrisonerID=5
but it gives me 4 rows, 9 rows when i have 3 rows of data in each Table etc.
i want rows of data with out duplicate. any suggestions or help will be highly appreciated.
It looks like at.PrisonerID = wt.PrisonerID in your query might be what is causing all of the duplicates. I am guessing AddPaymentTransaction and WithdrawPaymentTransation should not be linked together. So, how about the following:
SELECT at.PrisonerID, at.Amount as AAmount, at.Date as ADate,
wt.Amount as WAmount, wt.Date as WDate
FROM Prisoners p
INNER JOIN AddPaymentTransaction at p.PrisonerID = at.PrisonerID
INNER JOIN WithdrawPaymentTransation wt ON p.PrisonerID = wt.PrisonerID
WHERE at.PrisonerID = 5
but this probably isn't going to give you exactly what you are looking for either. So maybe something like the following:
SELECT * FROM
(
SELECT p.PrisonerID, 'AddPayment' AS Type,
apt.Amount as TransAmount, apt.Date AS TransDate
FROM Prisoners p
INNER JOIN AddPaymentTransaction apt ON p.PrisonerID = apt.PrisonerID
WHERE apt.PrisonerID = 5
UNION
SELECT p.PrisonerID, 'WithdrawPayment' AS Type,
wt.Amount as TransAmount, wt.Date as TransDate
FROM Prisoners p
INNER JOIN WithdrawPaymentTransation wt ON p.PrisonerID = wt.PrisonerID
WHERE wt.PrisonerID = 5
) AS mq
ORDER BY mq.TransDate DESC