简体   繁体   中英

Sql Server query that returns prices from each shop on each date and adds a 0 if no data is present for the shopId on a particular date

I have this Sql Server database table called productPrices:

 shopId int 
 price  decimal(18, 2)  
 dateFound  datetime    

I want to make an sql query formatted to use on a chart.js line chart. The line chart takes the following parameters:

An array of X Values - which in this context will be the date/day (from lowest to highest) Each shopId contains an array of prices.

I have made a codepen version of how the data should be displayed in the line chart (using just data added manually)

https://codepen.io/nickbuus/pen/OJxEGqK

The problem is that if no price was added for the shop on a particular day then a data value for instance 0 still needs to be present since price arrays length has to match the X Values array.

How could I make a query that fills out 0 when the particular shopId doesn't have a value on that particular date?

What is the best way to format the returned data from the sql query when it should be used for the structure that chart.js line chart uses?

Ideally what you should be doing here is creating a Calendar Table. I'm not going to cover how you create a calendar table here, as a search in your favourite search engine of something like "Calendar Table SQL Server" will give you a huge wealth of resources and give some great explanations and their use cases.

Once you have a Calendar table, you need to CROSS JOIN that to your Shops table; which I also assume you have. Then you can simply LEFT JOIN to your Prices table.

So a parametrised query might look like this:

SELECT S.ShopID,
       COUNT(P.Price) AS Prices,
       C.Date AS Datefound
FROM dbo.Calendar C
     CROSS JOIN dbo.Shops S
     LEFT JOIN dbo.Prices P ON C.CalendarDate = P.DateFound --Though DateFound is a datetime, I assume it's time portion is 00:00:00.000
                           AND C.ShopId = S.ShopID
WHERE C.CalendarDate >= @StartDate
  AND C.CalendarDate < DATEADD(DAY, 1, @EndDate)
GROUP BY S.ShopID,
         C.Date;

If you can't, for some reason, create a Calendar table and you don't have (and can't create) a Shop table (I strongly suggest you do create one though) then you'll need to use an inline tally to create your Calendar, and DISTINCT to get the Shop IDs.

A parametrised query would look something like this:

WITH N AS(
    SELECT N
    FROM (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL))N(N)),
Tally AS(
    SELECT 0 AS I
    UNION ALL
    SELECT TOP(DATEDIFF(DAY, @StartDate, @EndDate))
           ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS I
    FROM N N1, N N2, N N3), --1,000 rows. Add more cross joins for more rows
Calendar AS(
    SELECT DATEADD(DAY, T.I, @StartDate) AS CalendarDate
    FROM Tally T),
Shops AS(
    SELECT DISTINCT P.ShopID
    FROM dbo.Prices P)
SELECT S.ShopID,
       COUNT(P.Price) AS Prices,
       C.Date AS Datefound
FROM dbo.Calendar C
     CROSS JOIN dbo.Shops S
     LEFT JOIN dbo.Prices P ON C.CalendarDate = P.DateFound --Though DateFound is a datetime, I assume it's time portion is 00:00:00.000
                           AND C.ShopId = S.ShopID
GROUP BY S.ShopID,
         C.Date;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM