First of all, i hope you guys understand my poor english :))
I have a table like this
product | value | trx_date
apple | 100 | 2020-06-01
apple | 300 | 2020-06-02
apple | 500 | 2020-06-03
and i need create a report like this (lets say today is 2020-06-03)
product | yesterday | current_date | delta
apple | 300 | 500 | 200
im confused how to create a query (postgre), comparing those value.. fyi, i always update this table everyday.. i tried with ('1 day'::interval) query but it always show all date before 2020-06-03 which is 2020-06-01 and 2020-06-02..
i appreciate for your help..
Use the Window Function lead or lag to 'combine' data to the current row from following rows (lead) or previous rows (lag). In this case the I use the lag function to get "yesterdays" value.
select product, yesterday, today, today-yesterday delta
from ( select p.product, p.value today
, lag(value) over (partition by p.product
order by p.trx_date) yesterday
, p.trx_date
from products p
) d
where trx_date = '2020-06-03'::date ;
Using CTE:
https://www.postgresql.org/docs/12/queries-with.html
An example:
CREATE TABLE product_table (product varchar, value integer, trx_date date);
INSERT INTO product_table values ('apple', 100, '06/01/2020'), ('apple', 300, '06/02/2020'), ('apple', 500, '06/03/2020');
WITH prev AS (
SELECT
product,
value
FROM
product_table
WHERE
trx_date = '06/03/2020'::date - '1 day'::interval
)
SELECT
pt.product,
prev.value AS yesterday,
pt.value AS CURRENT_DATE,
pt.value - prev.value AS delta
FROM
product_table AS pt,
prev
WHERE
trx_date = '06/03/2020';
product | yesterday | current_date | delta
---------+-----------+--------------+-------
apple | 300 | 500 | 200
Related
I have a table consisting of ID, Year, Value
---------------------------------------
| ID | Year | Value |
---------------------------------------
| 1 | 2006 | 100 |
| 1 | 2007 | 200 |
| 1 | 2008 | 150 |
| 1 | 2009 | 250 |
| 2 | 2005 | 50 |
| 2 | 2006 | 75 |
| 2 | 2007 | 65 |
---------------------------------------
I then create a derived, aggregated table consisting of an ID, MinYear, and MaxYear
---------------------------------------
| ID | MinYear | MaxYear |
---------------------------------------
| 1 | 2006 | 2009 |
| 2 | 2005 | 2007 |
---------------------------------------
I then want to find the sum of Values between the MinYear and MaxYear foreach ID in the aggregated table, but I am having trouble determining a proper query.
The final table should look something like this
----------------------------------------------------
| ID | MinYear | MaxYear | SumVal |
----------------------------------------------------
| 1 | 2006 | 2009 | 700 |
| 2 | 2005 | 2007 | 190 |
----------------------------------------------------
Right now I can perform all the joins to create the second table. But then I use a fast forward cursor to iterate through each record of the second table with the code inside the for loop looking like the following
DECLARE #curMin int
DECLARE #curMax int
DECLARE #curID int
FETCH Next FROM fastCursor INTo #curISIN, #curMin , #curMax
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT Sum(Value) FROM ValTable WHERE Year >= #curMin and Year <= #curMax and ID = #curID
Group By ID
FETCH Next FROM fastCursor INTo #curISIN, #curMin , #curMax
Having found the sum of values between specified years, I can connect it back to the second table and I wind up the desired result (the third table).
However, the second table in reality is roughly 4 million rows, so this iteration is extremely time consuming (~generating 300 results a minute) and presumably not the best solution.
My question is, is there a way to generate the third table's results without having to use a cursor/for loop?
During a group by the sum will only be for the ID in question -- since the min year and max year is for the ID itself then you don't need to double query. The query below should give you exactly what you need. If you have a different requirement let me know.
SELECT ID, MIN(YEAR) as MinYear, MAX(YEAR) as MaxYear, SUM(VALUE) as SUMVALUE
FROM tablenameyoudidnotsay
GROUP BY ID
You could use query as bellow
TableA is your first table, and TableB is the second one
SELECT *,
(select SUM(Value) FROM TableA where tablea.ID=TableB.ID AND tableA.Year BETWEEN
TableB.MinYear AND TableB.MaxYear) AS SumValue
from TableB
You can put your criteria into a join and obtain the result all as one set which should be faster:
SELECT b.Id, b.MinYear, b.MaxYear, sum(a.Value)
FROM Table2 b
JOIN Table1 a ON a.Id=b.Id AND b.MinYear <= a.Year AND b.MaxYear >= a.Year
GROUP BY b.Id, b.MinYear, b.MaxYear
In SQL Server 2012, I have a table t1 where we store a list of excluded product.
I would like to add a column LastExclusionDate to store the date since when the product has been excluded.
Every day the product is inserted into the table if it is excluded. If not there will be no row and the next time when the product will be excluded there will be a gap date with the previous insert.
I would like to find a T-SQL query to update the LastExclusionDate column.
I would like to use it to populate column LastExclusionDate the first time (=initialisation) and use it every day to update the column when we insert a new row
I've tried this query, but I don't know how to get LastExclusionDate!
;WITH Cte AS
(
SELECT
product_id,
CreationDate,
LAG(CreationDate) OVER (PARTITION BY Product_ID ORDER BY CreationDate) AS GapStart,
(DATEDIFF(DAY, LAG(CreationDate) OVER (PARTITION BY Product_id ORDER BY CreationDate), CreationDate) -1) AS GapDays
FROM
#t1
)
SELECT *
FROM cte
Here's some sample data:
+------------+--------------+--------------------------------+
| product_id | CreationDate | LastExclusionDate_(toPopulate) |
+------------+--------------+--------------------------------+
| 100 | 2018-05-01 | 2018-05-01 |
| 100 | 2018-05-02 | 2018-05-01 |
| 100 | 2018-05-03 | 2018-05-01 |
| 100 | 2018-06-01 | 2018-06-01 |
| 100 | 2018-06-02 | 2018-06-01 |
| 200 | 2018-09-01 | 2018-09-01 |
| 200 | 2018-09-02 | 2018-09-01 |
| 200 | 2018-09-17 | 2018-09-17 |
+------------+--------------+--------------------------------+
Thanks
The idea in finding gap-less sequences is to compare the series to a gap-less sequence and find groups of records where the difference of both doesn't change. For example, when the date increases one by one and a row number also does, then the difference between both stays the same and we found a group:
WITH
cte (product_id, CreationDate, grp) AS (
SELECT product_id, CreationDate
, DATEDIFF(day, '19000101', CreationDate)
- ROW_NUMBER() OVER (PARTITION BY product_id ORDER BY CreationDate)
FROM #t1
)
SELECT product_id, CreationDate
, MIN(CreationDate) OVER (PARTITION BY product_id, grp) AS LastExclusionDate
FROM cte
For ongoing daily insertions it can be done with something like this.
INSERT INTO <yourTable>
SELECT
newProduct.[product_id],
newProduct.[creationDate],
isnull(existingProduct.[lastExclusionDate], newProduct.[creationDate]) AS [lastExclusionDate]
FROM
(SELECT <#product_id> AS [product_id], <#createionDate> AS [creationDate]) AS newProduct
LEFT JOIN #temp existingProduct
ON existingProduct.[product_id] = newProduct.product_id
AND existingProduct.[creationDate] = DATEADD(DAY,-1,newProduct.[creationDate])
I've got a demo here http://rextester.com/BDEO23118 . It's a larger than necessary demo because it uses the code above with the data you provided to populate a table row-by-row like you might in a daily update process. It then does individual insertions using this code with some new dates so you can see the way it handles new ranges. (just an FYI, rextester displays result dates in day.month.year hh:mm:ss format, but you can dump the script into management studio and it will output in DATE format)
My name is Thorsten and I'm new to SQL Server. Now I am facing a problem after setting a join... I joined two tables, and it worked so far, but I don't have enough knowledge to proceed.
Here is table1:
| Item | validDate | Price |
| ---- | --------- | ----- |
| A | 01.01.2017 | 100 |
| A | 31.03.2017 | 100 |
| A | 01.04.2017 | 120 |
| A | 31.07.2017 | 120 |
Now I want to create a table that includes a dataset for the gap in table1:
| Item | validDate | Price |
| ---- | --------- | ----- |
| A | 01.01.2017 | 100 |
| A | 28.02.2017 | 100 |
| A | 31.03.2017 | 120 |
... and so on.
My idea was to set a join from table1 to a date table, were every month end is included. But I have to insert the gap as well by creating a new dataset. With what code I'll be able to solve this issue?
As mentioned - I'm a beginner, so I hope I was able to describe my problem.
Thanks in advance for help!
Try to make use of below Query :
DECLARE #Table TABLE (Item VARCHAR(2), validDate DATE, Price INT)
INSERT INTO #Table VALUES
('A','2017-01-01',100),
('A','2017-03-31',100),
('A','2017-04-01',120),
('A','2017-07-31',120)
SELECT Item,DATEADD(DD,-1,validDate) AS Date,Price FROM #Table
OUTPUT
Item Date Price
A 2016-12-31 100
A 2017-03-30 100
A 2017-03-31 120
A 2017-07-30 120
I'm not sure if I understand you correctly but to fill the 'gaps' in your table you need to insert last days of each month. Here's the script that will do that for you. Since I'm fairly new in SQL too, this might not be the best solution, but worked for me. Please not that it will not insert records with dates that already exist in your validDate column:
declare #dateVar date = '2017-01-01' -- script will start calculating last day of the month from this date. DON'T modify the day value
declare #yearVar int = '2017' -- script insert months until the end of the year in this variable
declare #endDates table
(
item nvarchar(1),
endOfMonthDate date
)
while datepart(year, #dateVar) = #yearVar
begin
insert into #endDates
(item, endOfMonthDate)
values (
'A',
dateadd(day, -1, dateadd(month, 1, #dateVar))
)
set #dateVar = dateadd(month, 1, #dateVar)
end
insert into dbo.table1
(Item, validDate)
(
select item, endOfMonthDate
from #endDates
where endOfMonthDate not in (
select validDate
from table1)
)
Now, updating the records with the correct prices will be a little tricky. First we set up the Price for the last days of each month based on the price from the beginning of the month.
update dbo.table1
set table1.Price = t2.Price
from table1
left join table1 as t2
on month(table1.validDate) = MONTH(t2.validDate)
And then, we update the rest records that don't have prices with values from previous months:
declare #loopVar int = 0
declare #nullsNumb int = (select sum(case when table1.Price is null then 1 else 0 end) from table1) --calculates number of nulls in the Price column
while #loopVar < #nullsNumb --not so great solution that inserts previous month's price to every record that doesn't have any price at this moment
begin
update dbo.table1
set table1.Price = t2.Price
from table1
left join table1 as t2
on month(table1.validDate) = MONTH(t2.validDate) + 1
where table1.Price is null
set #loopVar = #loopVar + 1
end
Here's how the data in table1 look now when ordered by validDate:
Item validDate Price
A 2017-01-01 100
A 2017-01-31 100
A 2017-02-28 100
A 2017-03-31 100
A 2017-04-01 120
A 2017-04-30 120
A 2017-05-31 120
A 2017-06-30 120
A 2017-07-31 120
A 2017-08-31 120
A 2017-09-30 120
A 2017-10-31 120
A 2017-11-30 120
A 2017-12-31 120
Let me know if I was able to help.
I have a table in SQL server, returning the results below, I am having trouble carrying over my SQNCarriedOver Value into the ThisAppOFFICIAL column on the next application.
I cannot do it based on the row number because the Project Names do not list in that order:
I was wondering if there was a way to carry the QSNCarried over value to the ThisAppOFFICIAL column in the next application with that Project Name,
So for Example:
The thisAppOfficial value on "12/06/2016" would be "10" (from in the QSCarriedOver on "12/05/2016")
I may have overlooked something, I have searched the web but I feel my question is quite specific.
Any help or advice is appreciated, Thank you in advance.
ps:
the next application is the next Application Date with the Same Project Name
Query:
SQLFIDDLEExample
SELECT t.Project,
t.AppDate,
t.thisAppOfficial,
COALESCE((SELECT TOP 1 t2.QSCarriedOver
FROM X t2
WHERE t2.Project = t.Project
AND t2.AppDate < t.AppDate
ORDER BY t2.AppDate desc), 0) as NewColumn,
t.QSCarriedOver
FROM X t
Result:
| Project | AppDate | thisAppOfficial | NewColumn | QSCarriedOver |
|---------|------------|-----------------|-----------|---------------|
| A | 2016-04-13 | 30 | 0 | 0 |
| A | 2016-05-12 | 30 | 0 | 10 |
| A | 2016-06-12 | 30 | 10 | 0 |
| A | 2016-07-12 | 30 | 0 | 0 |
I think I've got what you want and I'm sure that there is a better way of doing it. I've created a simpler version of your table for my needs. I then use ROW_NUMBER to put the rows into a sequence so I can do a sort of self-join to the previous row to get the carry forward figure
CREATE TABLE X (Project varchar(50), AppDate date, thisAppOfficial int, QSCarriedOver int)
inserT INTO X VALUES('A', '13 Apr 2016', 30,0)
inserT INTO X VALUES('A', '12 May 2016', 30,10)
inserT INTO X VALUES('A', '12 Jun 2016', 30,0)
inserT INTO X VALUES('A', '12 Jul 2016', 30,0)
SELECT X0.Project, X0.AppDate, X0.thisAppOfficial, X0.QSCarriedOver, ISNULL(X2.QSCarriedOver,0) as 'Brought forward from previous row' FROM X X0
JOIN (select ROW_NUMBER () OVER (ORDER BY Project, AppDate) as MainRow, * from X) X1 ON X1.Project = X0.Project AND X1.AppDate = X0.AppDate
LEFT OUTER JOIN (select ROW_NUMBER () OVER (ORDER BY Project, AppDate) as PrevRow, * FROM X) X2 ON X2.Project = X1.Project and X2.PrevRow = MainRow -1
order by Project, AppDate
Have a try with this and see if it is doing what you need, I'm not 100% sure I've understood your requirements so no problem if it isn't what you want.
I am trying to compute efficiently (using SQL Server 2008) the moving average of the ProductCount over a period of 24 hours. For every single row in the Product table, I'd like to know what was the average of ProductCount (for that given products) over the last 24 hours. One problem with our data is that not all the dates/hours are present (see example below). If a TimeStamp is missing, it means that the ProductCount was 0.
I have a table with millions or rows with a Date, Product and Count. Below is a simplified example of the data I have to deal with.
Any idea on how to acheive that?
EDIT: One other piece of data that I need is the MIN and MAX ProductCount for the period (i.e. 24h). Computing the MIN/MAX is a bit trickier because of the missing values...
+---------------------+-------------+--------------+
| Date | ProductName | ProductCount |
+---------------------+-------------+--------------+
| 2012-01-01 00:00:00 | Banana | 15000 |
| 2012-01-01 01:00:00 | Banana | 16000 |
| 2012-01-01 02:00:00 | Banana | 17000 |
| 2012-01-01 05:00:00 | Banana | 12000 |
| 2012-01-01 00:00:00 | Apple | 5000 |
| 2012-01-01 05:00:00 | Apple | 6000 |
+---------------------+-------------+--------------+
SQL
CREATE TABLE ProductInventory (
[Date] DATETIME,
[ProductName] NVARCHAR(50),
[ProductCount] INT
)
INSERT INTO ProductInventory VALUES ('2012-01-01 00:00:00', 'Banana', 15000)
INSERT INTO ProductInventory VALUES ('2012-01-01 01:00:00', 'Banana', 16000)
INSERT INTO ProductInventory VALUES ('2012-01-01 02:00:00', 'Banana', 17000)
INSERT INTO ProductInventory VALUES ('2012-01-01 05:00:00', 'Banana', 12000)
INSERT INTO ProductInventory VALUES ('2012-01-01 00:00:00', 'Apple', 5000)
INSERT INTO ProductInventory VALUES ('2012-01-01 05:00:00', 'Apple', 6000)
Well, the fact that you need to calculate the average for every hour, actually makes this simpler, since you just need to SUM the product count and divide it by a fixed number (24). So I think that this will get the results you want (though in this particular case, a cursor by be actually faster):
SELECT A.*, B.ProductCount/24 DailyMovingAverage
FROM ProductInventory A
OUTER APPLY ( SELECT SUM(ProductCount) ProductCount
FROM ProductInventory
WHERE ProductName = A.ProductName
AND [Date] BETWEEN DATEADD(HOUR,-23,A.[Date]) AND A.[Date]) B
I added to Lamak's answer to include min/max:
SELECT *
FROM ProductInventory A
OUTER APPLY (
SELECT
SUM(ProductCount) / 24 AS DailyMovingAverage,
MAX(ProductCount) AS MaxProductCount,
CASE COUNT(*) WHEN 24 THEN MIN(ProductCount) ELSE 0 END AS MinProductCount
FROM ProductInventory
WHERE ProductName = A.ProductName
AND [Date] BETWEEN DATEADD(HOUR, -23, A.[Date]) AND A.[Date]) B
To account for missing records, check that there were indeed 24 records in the last 24 hours before using MIN(ProductCount), and return 0 otherwise.
Working SQL Fiddle, with a bunch (bushel?) of Oranges added to show the MinProductCount working