SQL Server join and insert data - sql-server

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.

Related

Compare current week to previous week

I have a list of members by week and I need to compare the current week to the previous in SQL Server.The first image is how the data is in table and the second image is what I want as a result. I thought maybe doing a CTE for each week and then comparing them. Thanks.
A very naive approach that counts all mem_id values that weren't in the previous week as new could look like this:
declare #t table(mem_id int,weeknum int, yearnum int);
insert into #t values(1,1,2020),(2,1,2020),(1,2,2020),(3,2,2020),(2,3,2020),(3,3,2020),(4,3,2020);
with p as
(
select yearnum
,weeknum
,case when lag(weeknum,1) over (partition by mem_id order by yearnum,weeknum) = weeknum-1 then 0 else 1 end as p
from #t
)
select yearnum
,weeknum
,sum(p) as new
,count(1) as total
from p
group by yearnum
,weeknum
order by yearnum
,weeknum;
Output
+---------+---------+-----+-------+
| yearnum | weeknum | new | total |
+---------+---------+-----+-------+
| 2020 | 1 | 2 | 2 |
| 2020 | 2 | 1 | 2 |
| 2020 | 3 | 2 | 3 |
+---------+---------+-----+-------+

Compare value between current date and yesterday on the same table POSTGRESQL

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

Altering vs adding column - setting all dates in month to one end date

I'm trying to add rank by sales and also change the date column to a 'month end' field that would have one month end date per month - if that makes sense?
Would you alter table and add column or could you just rename the date field and use set and case to make all March dates = 3-31-18 and all April 4-30-18?
I got this far:
UPDATE table1
SET DATE=EOMONTH(DATE) AS MONTH_END;
ALTER TABLE table1
ADD COLUMN RANK INT AFTER sales;
UPDATE table1
SET RANK=
RANK() OVER(PARTITION BY cust ORDER BY sales DESC);
LIMIT 2
can i do two sets in a row like that without adding an update? I'm looking for top 2 within each month - would this work? I feel like this is right and most efficient query, but its not working - any help appreciated!!
orig table
+------+----------+-------+--+
| CUST | DATE | SALES | |
+------+----------+-------+--+
| 36 | 3-5-2018 | 50 | |
| 37 | 3-15-18 | 100 | |
| 38 | 3-25-18 | 65 | |
| 37 | 4-5-18 | 95 | |
| 39 | 4-21-18 | 500 | |
| 40 | 4-45-18 | 199 | |
+------+----------+-------+--+
desired output
+------+-----------+-------+------+
| CUST | Month End | SALES | Rank |
+------+-----------+-------+------+
| | | | |
| 37 | 3-31-18 | 100 | 1 |
| 38 | 3-31-18 | 65 | 2 |
| 39 | 4-30-18 | 500 | 1 |
| 40 | 4-30-18 | 199 | 2 |
+------+-----------+-------+------+
Based on your expected output I think this may work as well.
create table Salesdate (Cust int, Dates date, Sales int)
insert into Salesdate values
(36 , '2018-03-05' , 50 )
,(37 , '2018-03-15' , 100 )
,(38 , '2018-03-25' , 65 )
,(37 , '2018-04-05' , 95 )
,(40 , '2018-04-25' , 199 )
,(39 , '2018-04-21' , 500 )
Updating the same column dates to the last day of the month (EOmonth will help to give last day of the month), you can add a separate column or update the column as you prefer.
Update Salesdate
set Dates = eomonth(Dates)
Add a column called rank in the table.
Alter table Salesdate
add rank int
Update the column rank which was just added.
update Salesdate
set Salesdate.[rank] = tbl.Ranked from
(select Cust, Sales, Dates , rank() over (Partition by Dates order by Sales Desc)
Ranked from Salesdate ) tbl
where tbl.Cust = salesdate.Cust
and tbl.Sales = salesdate.Sales
and tbl.dates = salesdate.Dates
--Not sure if this step is necessary if you want your final table to have only rank 1 and 2, then you can delete the data. Or it can be filtered out only on select list as well. Please note that sometimes rank may skip the number if we don't have unique set of sales amount for a given customer.
;With cte as (
select * from Salesdate)
delete from cte
where [RANK] > 2
select * from Salesdate
order by dates, [RANK]
Output
Cust Dates Sales rank
37 2018-03-31 100 1
38 2018-03-31 65 2
39 2018-04-30 500 1
40 2018-04-30 199 2

Update a column with LastExclusionDate

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)

How do i can show forecast years data from row into column?

Suppose if Item-A's Sale in 2013 is 100 Quantity and I'm expecting 10% of sales growth in next year i.e. in 2014
--------------------
ITEM | YEAR | QTY |
--------------------
ITM-A| 2013 | 100 |
ITM-B| 2013 | 200 |
--------------------
if I want to forecast sale data for up to year 2015
------------------------------
Item | 2013 | 2014 | 2015 |
------------------------------
Item-A | 100 | 110 | 121 |--each year qty incremented by 10% of its
Item-B | 200 | 220 | 242 |--previous year qty
------------------------------
try this,you have to use dynamic sql
Declare #toyear int=2016
Declare #forcast int=10
Declare #t table (ITEM varchar(50), years int, qty int)
insert into #t
select 'TM-A' ITEM , 2013 years, 100 qty
union all
select 'TM-B' ITEM , 2013 years, 200 qty
;with CTE1 as
(
select * from #t
union all
select b.ITEM,b.years+1,b.qty+((#forcast*b.qty)/100) from #t a
inner join cte1 b on a.ITEM=b.ITEM
and b.years<#toyear
)
select * from
(select * from cte1 )t4
pivot(min(qty) for years in([2013],[2014],[2015],[2016]))pvt
Try this:
select item,
qty as '2013',
round(qty*1.1) as '2014',
round(qty*1.21) as '2015'
from sale;
A dynamic query using stored procedure
DELIMITER $$
create procedure p (IN end INT(10))
BEGIN
declare start int;
declare fact FLOAT;
SET fact = 1.1;
SELECT year into start FROM sale limit 1;
SET #QUERY1 = CONCAT("SELECT ITEM, QTY AS '",start,"'");
WHILE start < end DO
SET start = start + 1;
SET #QUERY1 = CONCAT(#QUERY1," ,qty*",fact," as '", start,"'");
SET fact = fact *1.1;
END WHILE;
SET #QUERY1 = CONCAT(#QUERY1," from sale");
PREPARE stmt FROM #QUERY1;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END $$
DELIMITER ;
Output:
mysql> call p(2016);
+-------+------+-------+--------+---------+
| ITEM | 2013 | 2014 | 2015 | 2016 |
+-------+------+-------+--------+---------+
| itemA | 100 | 110.0 | 121.00 | 133.100 |
| itemB | 200 | 220.0 | 242.00 | 266.200 |
+-------+------+-------+--------+---------+
2 rows in set (0.00 sec)
Check this question:
Pass a function return to another in the same row
Your function is simply the multiplication by 1.1

Resources