1

I have the following code :

select 
Date,
Locations.Description as 'Site',
StockItems.Description as 'Item Description',
ItemQuantity,
CASE
WHEN DATEPART(hour,(Date))<12 THEN 'Before 12PM'
ELSE 'After 12PM'
END AS '12 OR AFTER',
DATENAME(WEEKDAY, (date)) as 'Day'
from Transactions 
left join TransactionsLine on TransactionsLine.TransactionID = Transactions.id
left join StockItems  on TransactionsLine.StockItemID = StockItems.ID
left join Departments on StockItems.DepartmentCode = Departments.Code
left join Locations on  Transactions.Location = Locations.Code
where DATEDIFF(DAY, CONVERT(datetime, Date,11), GETDATE()) <= 30
AND Departments.Description in ('Hot Food') 
and (ISNULL(dbo.TransactionsLine.StockItemID, '') <> '') 
AND (dbo.TransactionsLine.Vol <> - 1) 
AND (ISNULL(dbo.TransactionsLine.Department, '') <> '')

which worked good for a while but what i have now been asked is if I can change this so it has the daily Average.

I tired to do this with summing the ItemQuantity and doing a group but this keep erroing as i need to have the 12 OR AFTER section.

I even tired to "count" the days in said date range but that didn't work either as there are multiple transaction per day (I thought using distinct might fix that but failed) :(

Here is some conext if it helps

1st Monday Before 12 = Sold 1 Biscuits 1st Monday After 12 = Sold 2 Biscuits

2nd Monday Before 12 = Sold 3 Biscuits 2nd Monday After 12 = Sold 4 Biscuits

3rd Monday Before 12 = Sold 5 Biscuits 3rd Monday After 12 = Sold 6 Biscuits

4th Monday Before 12 = Sold 7 Biscuits 4th Monday After 12 = Sold 8 Biscuits

End Result Data

Monday Before 12 Avergae = 4 Monday After 12 Average = 5

i was just going to use the QTY / 4 but this doenst really work as the days in 30,60,90 varies


My end game is being able to have a table like this :

enter image description here

here is an example of data -

enter image description here

Im pretty sure what I need to do is sum the item QTY per item per store per day and then divide by how many "days" are in that month but for the life of me I keep failing :(

I wish every month had 4 Mondays etc so I could just divide the summed QTY by 4


@ Charlieface As for the data :

Correct I would like the average Monday results for all dates for example :

May – 2023 These are all Mondays 01/05/2023 08/05/2023 15/05/2023 22/05/2023 29/05/2023

If Site 1 sold Cookies

01/08 Before 12 - 2

08/05 Before 12 – 5

15/05 Before 12 – 6

22/05 Before 12 – 10

29/05 Before 12 – 20

01/08 After 12 - 1

08/05 After 12 – 6

15/05 After 12 – 7

22/05 After 12 – 9

29/05 After 12 – 3

The flat table I would like to see would be something like this : and would have more sites, days, Item Description with real data

+--------+--------+-------------+------------------+---------------+
| Site   | Day    | 12 OR AFTER | Item Description | Daily Average |
+--------+--------+-------------+------------------+---------------+
| Site 1 | Monday | BEFORE 12   | Cookies          | 8.6           |
+--------+--------+-------------+------------------+---------------+
| Site 1 | Monday | AFTER 12    | Cookies          | 5.2           |
+--------+--------+-------------+------------------+---------------+

The challenge im facing is pretty much trying to work out the best way / the way to

SUM the Items Sold and then divide them by how many “Days (Mondays, Tuesday …etc) are in the data range

  • 2
    Why do you `LEFT JOIN` to `dbo.TransactionsLine` and `dbo.Departments` when you define that multiple columns from that tables **must not** have a `NULL` value? It is *impossible* for a column to have a non-`NULL` value if no row was found. – Thom A May 31 '23 at 11:28
  • 1
    As an FYI, [3+ part naming on Columns will be Deprecated](https://wp.larnu.uk/3-part-naming-on-columns-will-be-deprecated/) and should be avoided. You are far better off aliasing your objects in the `FROM` and using those to qualify your columns. This avoids the use of a feature that will be deprecated and possibly removed in a future version, and makes your code much more succinct and readable. Also, your column `Date` lacks any prefixing. – Thom A May 31 '23 at 11:28
  • 1
    Sample data and expected results would help immensely. Do you just want a final aggregation, or do you want a running average? What other columns do you want to group/partition the average by? – Charlieface May 31 '23 at 11:40
  • i have just added some more information, with example of what im trying to achieve + what the data i have looks like thanks so much in advance! – James Smith May 31 '23 at 12:10
  • 3
    [Please do not upload images of code/data/errors when asking a question.](//meta.stackoverflow.com/q/285551) – Thom A May 31 '23 at 12:46
  • 1
    Your expected results appear to be some kind of report with merged cells. SQL Server doesn't do that, it can only give you a single flat table. Do you want eg the average Monday results for *all* dates, or do you want some kind of rolling average? – Charlieface May 31 '23 at 13:36
  • Hey CharlieFace, Sorry for being unclear, yeah I do understand that and I can get excel do make the report I need in the format I want (hopefully) I have added more context to the original thread – James Smith May 31 '23 at 22:19

1 Answers1

1

You need to group by the Site DateWithoutTime Day 12 OR AFTER and Item Description, and sum it up, then group again with using the DateWithoutTime and take the average.

  • It's more efficient if you add the primary/unique key of a table to the GROUP BY where possible.
  • To avoid repeating code, put the day and time calculations in a CROSS APPLY.
  • You don't need to check for nulls, as <> already excludes nulls.
  • The DATEDIFF calculation is going to prevent proper usage of indexes. Instead, move the Date reference to the one side of the comparison by itself, and use DATEADD instead.
  • Use short meaningful table aliases to make the code more readable.
SELECT
  Site,
  Day,
  [12 OR AFTER],
  [Item Description],
  AVG(1.0 * ItemQuantity) AS [Daily Average]
FROM (
    SELECT
      l.Code,
      si.ID,
      l.Description AS Site,
      v.DateWithoutTime,
      v.Day,
      v.[12 OR AFTER],
      si.Description as [Item Description],
      SUM(ItemQuantity) AS ItemQuantity
    FROM Transactions t
    JOIN TransactionsLine tl on tl.TransactionID = t.id
    JOIN StockItems si on tl.StockItemID = si.ID
    JOIN Departments d on si.DepartmentCode = d.Code
    JOIN Locations l on t.Location = l.Code
    CROSS APPLY (
        SELECT
          CASE
            WHEN DATEPART(hour, t.Date) < 12 THEN 'Before 12PM'
            ELSE 'After 12PM'
            END AS [12 OR AFTER],
          CAST(t.Date AS date) AS DateWithoutTime,
          DATENAME(WEEKDAY, t.Date) AS Day
    ) v
    WHERE t.Date < DATEADD(DAY, -30, CAST(CAST(GETDATE() AS date) AS datetime))
      AND d.Description in ('Hot Food') 
      AND tl.StockItemID <> ''
      AND tl.Vol <> - 1
      AND tl.Department <> ''
    GROUP BY
      l.Code,
      l.Description,
      si.ID,
      si.Description,
      v.[12 OR AFTER],
      v.Day,
      v.DateWithoutTime
) t
GROUP BY
  t.Code,
  t.Site,
  t.ID,
  t.[Item Description],
  t.[12 OR AFTER],
  t.Day;
Charlieface
  • 52,284
  • 6
  • 19
  • 43
  • Hey Charlieface, i did try using SQL to do the Average, but what i have found is that is averaging on a transaction level so example its doing it like 50 transactions with 1 and 20 transactions with 2 giving the average of 1.285714286, the average im looking for is across every Monday in the range with the same example we have sold 70 (50 transactions with 1 and 20 transactions with 2) / how ever many Mondays for the example of May there was 5 so then 70/5 is 14, so we are selling on average 14 x every Monday – James Smith Jun 01 '23 at 01:36
  • See edit. You need to group twice then, first including the date, then again without the date – Charlieface Jun 01 '23 at 01:46
  • it hates me, i fixed a few issues - adding an extra ")" to - the where clause removing the AS Site from the l.Description GROUP BY and now i get this :( ` Msg 207, Level 16, State 1, Line 48 Invalid column name 'Description'. Msg 207, Level 16, State 1, Line 50 Invalid column name 'Day'. Msg 207, Level 16, State 1, Line 3 Invalid column name 'Day'. ` – James Smith Jun 01 '23 at 02:04
  • this is looking really good :) the only issues that the SQL average is doing is if there is 0 sold on said day + After 12PM because its not appearing as a value its not being used in the average for example 3+4+5 = 12 / 3 = 4 but what i need to see is 0+3+4+5 = 12 / 5 = 3 not sure if this is even doable :( – James Smith Jun 01 '23 at 02:56
  • For that you would need to cross-join a calendar table and a two-row `(VALUES ('Before'), ('After'))` and then left-join everything else to that. Otherwise the row simply isn't there to make a calculation. That's a different question, although there are many duplicates https://stackoverflow.com/questions/54825778/how-to-get-missing-dates-with-0-value-in-sql-server https://stackoverflow.com/questions/12597143/query-for-how-to-add-the-missing-dates-in-sql – Charlieface Jun 01 '23 at 02:58
  • thanks mate you have helped me ALOT. i will do some digging into how to add the extra value, in theory the date is in the table but just said item was not sold on that date which im sure will throw another spanner in the works lol – James Smith Jun 01 '23 at 03:24
  • No that's the same issue. You need to flip your thinking around. Start with `FROM CalendarDates` (and double it up using a `CROSS JOIN (VALUES` as mentioned) then do `LEFT JOIN YourTable` to that based on date, so now you have at least one row for every date (or two with the cross join) – Charlieface Jun 01 '23 at 11:36