Detect and Set Column values between rows - sql

I have a table relating to products:
PRD_SLD table
ID DATE SALE_IND
3 2012 0
3 2013 0
3 2014 1
3 2014 1
3 2015 1
3 2016 0
3 2017 1
I would like my final results to look like this:
PRD_SLD table
ID DATE SALE_IND STRT END
3 2012 0 2012 2014
3 2013 0 2012 2014
3 2014 1 2014 2016
3 2014 1 2014 2016
3 2015 1 2014 2016
3 2016 0 2016 2017
3 2017 1 2017 2017
I currently have a working CTE for retrieving the rows in which the values change. this CTE returns this:
PRD_SLD table
ID DATE SALE_IND
3 2012 0
3 2014 1
3 2016 0
3 2017 1
So it returns the first instance of the value in the table, and returns every time the SALE_IND changes.
Is there a way to create a start and end date based off of the date column? I am still very new to this and was enrolled in an advanced course. I'm sure there is a better way to complete this but is there a way to do it with the CTE results i have created? I know there is a between function but i don't know how to implement it into this query

One method is to define groups of adjacent records. You don't have a solid ordering of the rows, but you do have just enough information for this to work -- assuming the indicator is constant in each year.
select t.*,
min(year) over (partition by id, sale_ind, seqnum - seqnum_s) as min_year,
max(year) over (partition by id, sale_ind, seqnum - seqnum_s) as max_year
from (select t.*,
dense_rank() over (partition by id order by date) as seqnum,
dense_rank() over (partition by id, sale_ind order by date) as seqnum_s
from t
) t;

Related

Linear Interpolation in SQL

I work with crashes and mileage for the same year which is Year in table. Crashes are are there for every record, but annual mileage is not. NULLs for mileage could be at the beginning or at the end of the time period for certain customer. Also, couple of annual mileage records can be missing as well. I do not know how to overcome this. I try to do it in CASE statement but then I do not know how to code it properly. Issue needs to be resolved in SQL and use SQL Server.
This is how the output looks like and I need to have mileage for every single year for each customer.
The info I am pulling from is proprietary database and the records themselves should be untouched as is. I just need code in query which will modify my current output to output where I have mileage for every year. I appreciate any input!
Year
Customer
Crashes
Annual_Mileage
2009
123
5
3453453
2010
123
1
NULL
2011
123
0
54545
2012
123
14
376457435
2013
123
3
63453453
2014
123
4
NULL
2015
123
15
6346747
2016
123
0
NULL
2017
123
2
534534
2018
123
7
NULL
2019
123
11
NULL
2020
123
15
565435
2021
123
12
474567546
2022
123
7
NULL
Desired Results
Year
Customer
Crashes
Annual_Mileage
2009
123
5
3453453
2010
123
1
175399 (prior value is taken)
2011
123
0
54545
2012
123
14
376457435
2013
123
3
63453453
2014
123
4
34900100 (avg of 2 adjacent values)
2015
123
15
6346747
2016
123
0
3440641 (avg of 2 adjacent values)
2017
123
2
534534
2018
123
7
534534 ( prior value is taken)
2019
123
11
549985 (avg of 2 adjacent values)
2020
123
15
565435
2021
123
12
474567546
2022
123
7
474567546 (prior value is taken)
SELECT Year,
Customer,
Crashes,
CASE
WHEN Annual_Mlg IS NOT NULL THEN Annual_Mlg
WHEN Annual_Mlg IS NULL THEN
CASE
WHEN PREV.Annual_Mlg IS NOT NULL
AND NEXT.Annual_Mlg IS NOT NULL
THEN ( PREV.Annual_Mlg + NEXT.Annual_Mlg ) / 2
ELSE 0
END
END AS Annual_Mlg
FROM #table
The above code doesn't work, but I just need to start somehow and that what I have currently.
I understand what I need to do I just do not know how to code it in SQL.
After i applied row_number () function i got this output for first 2 clients and for the rest of the 4 clients row_number() function gave correct output. i have no idea why is that. I thought may be because i used "full join" before to combine mileage and crashes table?
enter image description here
Your use of #table tells me that you're using MS SQL Server (a temporary table, probably in a stored procedure).
You want to:
select all the rows in #table
joined with the matching row (if any) for the previous year, and
joined with the matching row (if any) for the next year
Then it's easy. Assuming the primary key on your #table is composed of the year and customer columns, something like this ought to do you:
select t.year ,
t.customer ,
t.crashes ,
annual_milage = coalesce(
t.annual_milage ,
( coalesce( p.annual_mileage, 0 ) +
coalesce( n.annual_mileage, 0 )
) / 2
)
from #table t -- take all the rows
left join #table p on p.year = t.year - 1 -- with the matching row for
and p.customer = t.customer -- the previous year (if any)
left join #table n on n.year = t.year + 1 -- and the matching row for
and n.customer = t.customer -- the next year (if any)
Notes:
What value you default to if the previous or next year doesn't exist is up to you (zero? some arbitrary value?)
Is the previous/next year guaranteed to be the current year +/- 1?
If not, you may have to use derived tables as the source for the
prev/next data, selecting the closest previous/next year (that sort
of thing rather complicates the query significantly).
Edited To Note:
If you have discontiguous years for each customer such that the "previous" and "next" years for a given customer are not necessarily the current year +/- 1, then something like this is probably the most straightforward way to find the previous/next year.
We use a derived table in our from clause, and assign a sequential number in lieu of year for each customer, using the ranking function row_number() function. This query, then
select row_nbr = row_number() over (
partition by x.customer
order by x.year
) ,
x.*
from #table x
would produce results along these lines:
row_nbr
customer
year
...
1
123
1992
...
2
123
1993
...
3
123
1995
...
4
123
2020
...
1
456
2001
...
2
456
2005
...
3
456
2020
...
And that leads us to this:
select year = t.year ,
customer = t.customer ,
crashes = t.crashes ,
annual_mileage = coalesce(
t.mileage,
coalesce(
t.annual_mileage,
(
coalesce(p.annual_mileage,0) +
coalesce(n.annual_mileage,0)
) / 2
),
)
from (
select row_nbr = row_number() over (
partition by x.customer
order by x.year
) ,
x.*
from #table x
) t
left join #table p on p.customer = t.customer and p.row_nbr = t.row_nbr-1
left join #table n on n.customer = t.customer and n.row_nbr = t.row_nbr+1

Getting Monthly Data

I want to extract all budget entries charged to the current year and cumulated over each month after .In January, taking the total over January, February take accumulated of January plus accumulated February...
I started with this query :
IF OBJECT_ID('tempdb..#BudgetTransTmp') IS NOT NULL
DROP TABLE #BudgetTransTmp
Select
Row_number() over(ORDER BY YEAR(BTLine.DATE),MONTH(BTLine.DATE)) as RowNumber,
COMBINATION.DISPLAYVALUE,
BTLine.LedgerDimension AS LedgerDimension,
MIN(BTLine.TransactionCurrencyAmount) AS Amount,
SUM(BTLine.TransactionCurrencyAmount)
OVER (ORDER BY YEAR(BTLine.DATE),MONTH(BTLine.DATE),BTLine.LedgerDimension,COMBINATION.DISPLAYVALUE ) AS SUM,
YEAR(BTLine.DATE) AS Year ,
MONTH(BTLine.DATE) AS MONTH
INTO #BudgetTransTmp
FROM MicrosoftDynamicsAX.dbo.BudgetTransactionLine AS BTLine
--Get Display value
INNER JOIN MicrosoftDynamicsAX.dbo.DIMENSIONATTRIBUTEVALUECOMBINATION AS COMBINATION
ON COMBINATION.RECID = BTLine.LEDGERDIMENSION
GROUP BY
BTLine.LedgerDimension,
YEAR(BTLine.DATE),
MONTH(BTLine.DATE)
ORDER BY RowNumber
The result is :
LedgerDimension Amount SUM Year Month Display
1 22565448266 850.00 850.00 2012 8 601200-001-027--
2 22565448265 1700.00 2550.0 2012 12 601200-002-027--
3 22565448266 2700.00 5250.00 2012 12 601200-001-027--
4 22565448267 650.00 5900.00 2012 12 601400-002-027--
5 22565448268 1100.00 7000.00 2012 12 601400-001-027--
But i want to get
LedgerDimension Amount SUM Year Month Display
1 22565448266 850.00 850.00 2012 8 601200-001-027--
2 22565448265 1700.00 1700.0 2012 12 601200-002-027--
3 22565448266 2700.00 3350.00 2012 12 601200-001-027--
4 22565448267 650.00 650.00 2012 12 601400-002-027--
5 22565448268 1100.00 1100.00 2012 12 601400-001-027--
I think my COMBINATION of ORDER by (primary key) must be betwwen LedgerDimension ,Year , Month , Display
Any help in this regards
I think what you need to do is:
SUM(BTLine.TransactionCurrencyAmount)
OVER (PARTITION BY BTLine.LedgerDimension ORDER BY YEAR(BTLine.DATE),MONTH(BTLine.DATE),BTLine.LedgerDimension,COMBINATION.DISPLAYVALUE ) AS SUM
let me know if this works.

firebird sql order by

i got a dataset which looks like this:
customernumber year value
1 2011 500
2 2011 100
1 2010 400
3 2010 600
3 2011 300
2 2010 700
i want it to be ordered by highest value of year 2011, but the rows of each customer need to stay together.
it should look like this:
customernumber year value
1 2011 500
1 2010 400
3 2011 300
3 2010 600
2 2011 100
2 2010 700
is this even possible?
thanks in advance!
Use join to bring that value in, and then you can use it for the oder by:
select d.customernumber, d.year, d.value
from dataset d join
(select d.*
from dataset d
where d.year = 2011
) d2011
on d.customernumber = d2011.customernumber
order by d2011.value, d.customernumber, d.year desc;
In databases that support window functions, this can more easily be done as:
select d.*
from dataset d
order by max(case when year = 2011 then value end) over (partition by customernumber),
customernumber, year desc;
select customernumber, year, value from mytable
group by customernumber, year, value
order by year desc

Oracle SQL Query -> Count 2 columns under 2 different conditions

I have a table that looks like this:
YEAR RESOLUTION_DATE CREATION_DATE
2013 2013/02/18
2012 2012/05/26
2009 2009/11/11
2013 2013/12/08 2013/12/01
2000 2000/17/31
2007 2007/12/08
2012 2012/12/08
2012 2012/03/23 2012/03/10
2012 2012/12/08
2007 2007/01/17
2012 2012/01/17 2012/01/10
2009 2009/02/14
I am trying to make a query that will output the following:
YEAR COUNT_RESOLUTION_DATE COUNT_CREATION_DATE
2000 0 1
2007 0 2
2009 0 2
2011 0 0
2012 2 5
2013 1 2
The caveat is that I would like the query to count the number of RESOLUTION_DATE by YEAR, where the RESOLUTION_DATE IS NOT NULL and i want to count ALL CREATION_DATE's. The SQL is needed for an oracle database.
Try this:
SELECT
COUNT(RESOLUTION_DATE) AS COUNT_RESOLUTION_DATE,
COUNT(CREATION_DATE) AS COUNT_CREATION_DATE
FROM MyTable
GROUP BY YEAR
ORDER BY YEAR
If you only want the non-NULL resolution dates counted, this should work:
SELECT
SUM(CASE WHEN RESOLUTION_DATE IS NULL THEN 0 ELSE 1 END) AS COUNT_RESOLUTION_DATE,
COUNT(CREATION_DATE) AS COUNT_CREATION_DATE
FROM MyTable
GROUP BY YEAR
ORDER BY YEAR;

Generate year to date by month report in SQL [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Running total by grouped records in table
I am trying to put together an SQL statement that returns the SUM of a value by month, but on a year to date basis. In other words, for the month of March, I am looking to get the sum of a value for the months of January, February, and March.
I can easily do a group by to get a total for each month by itself, and potentially calculate the year to date value I need in my application from this data by looping through the results set. However, I was hoping to have some of this work handled with my SQL statement.
Has anyone ever tackled this type of problem with an SQL statement, and if so, what is the trick that I am missing?
My current sql statement for monthly data is similar to the following:
Select month, year, sum(value) from mytable group by month, year
If I include a where clause on the month, and only group by the year, I can get the result for a single month that I am looking for:
select year, sum(value) from mytable where month <= selectedMonth group by year
However, this requires me to have a particular month pre-selected or to utilize 12 different SQL statements to generate one clean result set.
Any guidance that can be provided would be greatly appreciated!
Update: The data is stored on an IBM iSeries.
declare #Q as table
(
mmonth INT,
value int
)
insert into #Q
values
(1,10),
(1,12),
(2,45),
(3,23)
select sum(January) as UpToJanuary,
sum(February)as UpToFebruary,
sum(March) as UpToMarch from (
select
case when mmonth<=1 then sum(value) end as [January] ,
case when mmonth<=2 then sum(value) end as [February],
case when mmonth<=3 then sum(value) end as [March]
from #Q
group by mmonth
) t
Produces:
UpToJanuary UpToFebruary UpToMarch
22 67 90
You get the idea, right?
NOTE: This could be done easier with PIVOT tables but I don't know if you are using SQL Server or not.
As far as I know DB2 does support windowing functions although I don't know if this is also supported on the iSeries version.
If windowing functions are supported (I believe IBM calls them OLAP functions) then the following should return what you want (provided I understood your question correctly)
select month,
year,
value,
sum(value) over (partition by year order by month asc) as sum_to_date
from mytable
order by year, month
create table mon
(
[y] int not null,
[m] int not null,
[value] int not null,
primary key (y,m))
select a.y, a.m, a.value, sum(b.value)
from mon a, mon b
where a.y = b.y and a.m >= b.m
group by a.y, a.m, a.value
2011 1 120 120
2011 2 130 250
2011 3 500 750
2011 4 10 760
2011 5 140 900
2011 6 100 1000
2011 7 110 1110
2011 8 90 1200
2011 9 70 1270
2011 10 150 1420
2011 11 170 1590
2011 12 600 2190
You should try to join the table to itself by month-behind-a-month condition and generate a synthetic month-group code to group by as follows:
select
sum(value),
year,
up_to_month
from (
select a.value,
a.year,
b.month as up_to_month
from table as a join table as b on a.year = b.year and b.month => a.month
)
group by up_to_month, year
gives that:
db2 => select * from my.rep
VALUE YEAR MONTH
----------- ----------- -----------
100 2011 1
200 2011 2
300 2011 3
400 2011 4
db2 -t -f rep.sql
1 YEAR UP_TO_MONTH
----------- ----------- -----------
100 2011 1
300 2011 2
600 2011 3
1000 2011 4