I have a table for Inventory Dollars by Vendor by Month. I want to be able to update the dollar amounts for the current month on a daily basis, but I don't want to lose the previous month's data. Here is the basic query I have:
DELETE Inventory_Dollars
FROM Inventory_Summary
WHERE MonthNum = '4'
SELECT
SUM(Cost*OnHand) AS Inventory_Dollars
FROM Inventory
The Inventory table will always hold the current data. How can I just Insert Into Inventory_Summary the data from the Select statement?
Just preface your query with an INSERT:
INSERT INTO Inventory_Summary
(Inventory_Dollars)
SELECT SUM(Cost * OnHand) AS Inventory_Dollars
FROM Inventory
If you've already inserted the inventory_dollars amount for the current month, you can then update the value every day with something like this:
UPDATE Inventory_Summary
SET Inventory_Dollars = (
SELECT (Cost * OnHand)
FROM Inventory
)
WHERE MonthNum = DATEPART(m, GETDATE()) AND Year = DATEPART(year, GETDATE())
The DATEPART can be used to fill in the number of the month for the current date, GETDATE(). Then you won't be updating the inventory_dollars values for past months.
Edit: Also added a year to the where clause, so you don't update months from past years.
Edit 2: If you use a subquery in the SET, make sure only one result can come back.
Related
I have problem select data between two dates if the only start_date is available.
The example I want to see is what discount_nr was active between 2020-07-01 and 2020-07-15 or only one day 2020-07-14. I tried different solutions, date range, generate series, and so on, but was still not able to get it to work.
Table only have start dates, no end dates
Example:
discount_nr, start_date
1, 2020-06-30
2, 2020-07-03
3, 2020-07-10
4, 2020-07-15
You can get the end dates by looking at the start date of the next row. This is done with lead. lead(start_date) over(order by start_date asc) will get you the start_date of the next row. If we take 1 day from that we'll get the inclusive end date.
Rather than separate start/end columns, a single daterange column is easier to work with. You can use that as a CTE or create a view.
create view discount_durations as
select
id,
daterange(
start_date,
lead(start_date) over(order by start_date asc)
) as duration
from discounts
Now querying it is easy using range operators. #> to check if the range contains a date.
select *
from discount_durations
where duration #> '2020-07-14'::date
And use && to see if they have any overlap.
select *
from discount_durations
where duration && daterange('2020-07-01', '2020-07-15');
Demonstration
I have an AWS Redshift table looking like this:
id, id_aw_sk, id_ai_sk, snapshot_date, update_timestamp
3278059021, 3197624, 173642, today-1, today
3278059021, 3197624, 173642, today-2, today-1
3278059021, 3197624, 173642, today-3, today-2
3278059021, 3197624, 173642, today-4, today-3
etc.
3278059021, 3224904, 173642, date in past -1, date in past
This table contains snapshot on every day, to see changes in some other columns. if there's a change id_aw_sk would be different than the previous one.
What seems to be the issue is that I have some date gaps for some rows, accidently deleted rows.
As i can't retrieve those, I would like to "create" them by finding gaps in dates.
I am not sure how to do this. Please, help?
I understand that i should firstly find the gaps and for each row i would use lead function to update values from current (known) rows.
e.g. i have dates for 3278059021 where id_aw_sk was 3224904, but i have gaps for dates between 16th March 2021 until 11th April 2021 for id_aw_sk 3197624.
I know that all rows between those dates haven't changed. I only need to populated gaps with first known data (from 11th April) as rows from 16th March and later are the same even now.
I hope that I explained it okay :)
Thanks upfront for your help.
I've found a gap like this:
select *, case when lag(snapshot_date) over (partition by site_id, site_aw_sk order by snapshot_date ASC)<>snapshot_date-1
then 1 else 0 end as gap
from
dwh.fact_site_ps where site_id=3278059021;
that way I can know where are the gaps but don't know how to populate them with data that's missing.
I've solved it.
Step 1:
create table dwh_dev.tmp_fact_site_ps_gaps as
select t.*,case when gap=1 then snapshot_date else null end as gap_start,
case when gap=1 then lead(snapshot_date) over (partition by site_id, site_aw_sk order by snapshot_date asc) else null end as gap_end
from
(select *, case when lead(snapshot_date) over (partition by site_id, site_aw_sk order by snapshot_date ASC)<>snapshot_date+1
then 1 else 0 end as gap
from
dwh.fact_site_ps)t
step 2:
select tfspg.*, tdr."date"
from dwh.tmp_date_range tdr
inner join (select * from dwh_dev.tmp_fact_site_ps_gaps where gap=1)tfspg
on tdr."date" > tfspg.gap_start AND tdr."date" < tfspg.gap_end
where site_id=3278059021
order by site_id
dwh.tmp_date_range is only a table with all dates for the last 2 years.
I need to find a date that is 11 business days after a date.
I did not have a date table. Requested one, long lead time for one.
Used a CTE to produce results that have a datekey, 1 if weekday, and 1 if holiday, else 0. Put those results into a Table Variable, now Business_Day is (weekday-holiday). Much Googling has already happened.
select dt.Datekey,
(dt.Weekdaycount - dt.HolidayCount) as Business_day
from #DateTable dt[enter image description here][1]
UPDATE, I've figured it out in Excel. Running count of business days, a column of business day count + 11, then a Vlookup finding the +11 date . Now how do I do that in SQL?
Results like this
Datekey
2019-01-01
Business_day 0
Datekey
2019-01-02
Business_day
1
I will assume you want to set your weekdays, and you can enter the holidays in a variable table, so you can do the below:-
here set the weekend names
Declare #WeekDayName1 varchar(50)='Saturday'
Declare #WeekDayName2 varchar(50)='Sunday'
Set the holiday table variable, you may have it as a specific table your database
Declare #Holidays table (
[Date] date,
HolidayName varchar(250)
)
Lets insert a a day or two to test it.
insert into #Holidays values (cast('2019-01-01' as date),'New Year')
insert into #Holidays values (cast('2019-01-08' as date),'some other holiday in your country')
lets say your date you want to start from is action date and you need 11 business days after it
Declare #ActionDate date='2018-12-28'
declare #BusinessDays int=11
A recursive CTE to count the days till you get the correct one.
;with cte([date],BusinessDay) as (
select #ActionDate [date],cast(0 as int) BusinessDay
union all
select dateadd(day,1,cte.[date]),
case
when DATENAME(WEEKDAY,dateadd(day,1,cte.[date]))=#WeekDayName1
OR DATENAME(WEEKDAY,dateadd(day,1,cte.[date]))=#WeekDayName2
OR (select 1 from #Holidays h where h.Date=dateadd(day,1,cte.[date])) is not null
then cte.BusinessDay
else cte.BusinessDay+1
end BusinessDay
From cte where BusinessDay<#BusinessDays
)
--to see the all the dates till business day + 11
--select * from cte option (maxrecursion 0)
--to get the required date
select MAX([date]) from cte option (maxrecursion 0)
In my example the date I get is as below:-
ActionDate =2018-12-28
After 11 business days :2019-01-16
Hope this helps
1st step was to create a date table. Figuring out weekday verse weekends is easy. Weekdays are 1, weekends are 0. Borrowed someone else's holiday calendar, if holiday 1 else 0. Then Business day is Weekday-Holiday = Business Day. Next was to create a running total of business days. That allows you to move from whatever running total day you're current on to where you want to be in the future, say plus 10 business days. Hard coded key milestones in the date table for 2 and 10 business days.
Then JOIN your date table with your transaction table on your zero day and date key.
Finally this allows you to make solid calculations of business days.
WHERE CONVERT(date, D.DTRESOLVED) <= CONVERT(date, [10th_Bus_Day])
I have a calendar table called CalendarInformation that gives me a list of dates from 2015 to 2025. This table has a column called BusinessDay that shows what dates are weekends or holidays. I have another table called OpenProblemtimeDiffTable with a column called number for my problem number and a date for when the problem was opened called ProblemNew and another date for the current column called Now. What I want to do is for each problem number grab its date ranges and find the dates between and then sum them up to give me the number of business days. Then I want to insert these values in another table with the problem number associated with the business day.
Thanks in advance and I hope I was clear.
TRUNCATE TABLE ProblemsMoreThan7BusinessDays
DECLARE #date AS date
DECLARE #businessday AS INT
DECLARE #Startdate as DATE, #EndDate as DATE
DECLARE CONTACT_CURSOR CURSOR FOR
SELECT date, businessday
FROM CalendarInformation
OPEN contact_cursor
FETCH NEXT FROM Contact_cursor INTO #date, #businessday
WHILE (##FETCH_STATUS=0)
BEGIN
SELECT #enddate= now FROM OpenProblemtimeDiffTable
SELECT #Startdate= problemnew FROM OpenProblemtimeDiffTable
SET #Date=#Startdate
PRINT #enddate
PRINT #startdate
SELECT #businessday= SUM (businessday) FROM CalendarInformation WHERE date > #startdate AND date <= #Enddate
INSERT INTO ProblemsMoreThan7BusinessDays (businessdays, number)
SELECT #businessday, number
FROM OpenProblemtimeDiffTable
FETCH NEXT FROM CONTACT_CURSOR INTO #date, #businessday
END
CLOSE CONTACT_CURSOR
DEALLOCATE CONTACT_CURSOR
I tried this code using a cursor and I'm close, but I cannot get the date ranges to change for each row.
So if I have a problemnumber with date ranges between 02-07-2018 and 05-20-2019, I would want in my new table the sum of business days from the calendar along with the problem number. So my output would be column number PROB0421 businessdays (with the correct sum). Then the next problem PRB0422 with date ranges of 11-6-18 to 5-20-19. So my output would be PROB0422 with the correct sum of business days.
Rather than doing this in with a cursor, you should approach this in a set based manner. That you already have a calendar table makes this a lot easier. The basic approach is to select from your data table and join into your calendar table to return all the rows in the calendar table that sit within your date range. From here you can then aggregate as you require.
This would look something like the below, though apply it to your situation and adjust as required:
select p.ProblemNow
,p.Now
,sum(c.BusinessDay) as BusinessDays
from dbo.Problems as p
join dbo.calendar as c
on c.CalendarDate between p.ProblemNow and p.Now
and c.BusinessDay = 1
group by p.ProblemNow
,p.Now
I think you can do this without a cursor. Should only require a single insert..select statement.
I assume your "businessday" column is just a bit or flag-type field that is 1 if the date is a business day and 0 if not? If so, this should work (or something close to it if I'm not understanding your environment properly).:
insert ProblemsMoreThan7BusinessDays
(
businessdays
, number
)
select
number
, sum( businessday ) -- or count(*)
from OpenProblemtimeDiffTable op
inner join CalendarInformation ci on op.problem_new >= ci.[date]
and op.[now] <= ci.[date]
and ci.businessday = 1
group by
problem_number
I usually try to avoid the use of cursors and working with data in a procedural manner, especially if I can handle the task as above. Dont think of the data as 1000's of individual rows, but think of the data as only two sets of data. How do they relate?
I have a table with a row for each sale of a product. These rows include a date. I want to know the number of products sold for each distinct day in a range (user specifies the begin and end dates.) There is a distinct row for each sale, so on days where several products were sold, several rows with this date exist. I want to count the number of these rows, with the same date. How might this be done efficiently in postgresql?
Example
2015-01-02: 0
2015-01-03: 7
2015-01-04: 2
Assuming the date column is stored as a datetime, something like this should get you in the right direction:
SELECT date_trunc('day', TIMESTAMP sales.date) as day, COUNT(*)
FROM sales
WHERE day >= start and <= end
GROUP BY day;
where start and end are filled in by the user.
More documentation for postgres's date features found here:
http://www.postgresql.org/docs/9.4/static/functions-datetime.html
If they are not datetime, you can use the to_date function with the appropriate format string to convert to datetime and the use the solution above:
SELECT date_trunc('day', to_date('2015-01-02', 'YYYY-MM-DD'));
http://www.postgresql.org/docs/9.4/static/functions-formatting.html