Cumulative multiplication with window functions, 'exp' is not a valid windowing function - sql

Is it possible doing cumulative multiply(below query) with window functions
select Id, Qty
into #temp
from(
select 1 Id, 5 Qty
union
select 2, 6
union
select 3, 3
)dvt
select
t1.Id
,exp(sum(log( t2.Qty))) CumulativeMultiply
from #temp t1
inner join #temp t2
on t2.Id <= t1.Id
group
by t1.Id
order
by t1.Id
Like:
select
t1.Id
,exp(sum(log( t2.Qty))) over (partition by t1.Id order by t1.Id rows between unbounded preceding and current row ) CumulativeMultiply
from #temp t1
inner join #temp t2
on t2.Id <= t1.Id
But get error:
The function 'exp' is not a valid windowing function, and cannot be used with the OVER clause
Update:
Result that actually I want:
Id CumulativeMultiply
----------- ----------------------
1 5
2 30
3 90

no need of self join for Sum Over(Order by) to find the previous records and multiply it
select
Id
,exp(sum(log( Qty))
over (order by Id )) CumulativeMultiply from #temp

Only aggregation function are valid windowing functions.
I didn't test the code, but you need to separate the 2 in a way:
SELECT Id, exp(cm) CumulativeMultiply
FROM (
select
Id
,sum(log(Qty)) over (partition by Id order by Id rows between unbounded preceding and current row ) cm
from #temp
) d

Related

Can I nest a select statement within an IF function in SQL?

Using Teradata..
I want to write a query that joins table 1 and table 2 on item code to the location in table 2.
There are multiple locations per item code and potentially multiple item code entries per location depending on date. I'm only interested in the most recent item per location. To achieve this I've used a nested query to select the max date per both location and item number. I'm still returning more rows of data than anticipated and suspect it is due to some duplicate locations slipping through, potentially with two different item numbers.
I'm wondering if its possible to use the IF operator to say "If there are duplicate locations, choose the location with the more recent date"
Is this possible?
Here is what I have written so far:
SELECT t1.item_no, t1.date, t2.location, t2.date
FROM table 1 t1
JOIN table 2 t2 ON t1.item_no = t2.item_no
WHERE (t1.item_no, t1.date) IN
(
SELECT item_no, MAX(date)
FROM table 1
GROUP BY item_no
)
AND (t2.location, t2.date) IN
(
SELECT location, MAX(date)
FROM table 2
GROUP BY location
)
Change your query and use Subquery
SELECT t1.item_no, t1.date, t2.location, t2.date FROM
(
SELECT item_no, MAX(date) date
FROM table 1
GROUP BY item_no
) T1
JOIN
(
SELECT location, MAX(date) date
FROM table 2
GROUP BY location
) T2
ON t1.item_no = t2.location
Without knowing DBMS, a solution could be to use ROW_NUMBER(). I'm not sure if there's a preference for nested queries over say CTE but a solution w/ CTE could be:
WITH items AS (
SELECT
item_no
,date AS item_date
,row_number() OVER (PARTITION BY item_no ORDER BY date desc) as rn
FROM table1
),
locations AS (
SELECT
location
,item_no
,date AS location_date
,ROW_NUMBER() OVER(PARTITION BY item_no, location ORDER BY date desc) as rn
from table2
)
SELECT
t1.item_no
,t1.item_date
,t2.location
,t2.location_date
FROM items AS t1
JOIN locations AS t2 on t1.item_no = t2.item_no
AND t2.rn = 1
WHERE t1.rn = 1

Calculating cumulative sum in ms-sql

I have a table tblsumDemo with the following structure
billingid qty Percent_of_qty cumulative
1 10 5 5
2 5 8 13(5+8)
3 12 6 19(13+6)
4 1 10 29(19+10)
5 2 11 40(11+10)
this is what I have tried
declare #s int
SELECT billingid, qty, Percent_of_qty,
#s = #s + Percent_of_qty AS cumulative
FROM tblsumDemo
CROSS JOIN (SELECT #s = 0) AS var
ORDER BY billingid
but I'm not able to get the desired output,any help would be much appreciated , Thanks
You can use CROSS APPLY:
SELECT
t1.*,
x.cumulative
FROM tblSumDemo t1
CROSS APPLY(
SELECT
cumulative = SUM(t2.Percent_of_Qty)
FROM tblSumDemo t2
WHERE t2.billingid <= t1.billingid
)x
For SQL Server 2012+, you can use SUM OVER():
SELECT *,
cummulative = SUM(Percent_of_Qty) OVER(ORDER BY billingId)
FROM tblSumDemo
You can use subquery which works in all versions:
select billingid,qty,percentofqty,
(select sum(qty) from tblsumdemo t2 where t1.id<=t2.id) as csum
from
tblsumdemo t1
you can use windows functions as well from sql 2012:
select *,
sum(qty) over (order by qty rows between unbounded PRECEDING and current row) as csum
from tblsumdemo
Here i am saying get me sum of all rows starting from first row for every row(unbounded preceeding and current row).you can ignore unbounded preceeding and current row which is default
Use ROW_NUMBER just to order the billingID in ascending order, then Use join.
Query
;with cte as(
select rn = row_number() over(
order by billingid
), *
from tblSumDemo
)
select t1.billingid, t1.qty, t1.Percent_of_qty,
sum(t2.Percent_of_qty) as cummulative
from cte t1
join cte t2
on t1.rn >= t2.rn
group by t1.billingid, t1.qty, t1.Percent_of_qty;

SQL Joining table with Min and Sec Min row

I want to join table 1 with table2 twice becuase I need to get the first minimum record and the second minimum. However, I can only think of using a cte to get the second minimum record. Is there a better way to do it?
Here is the table table:
I want to join Member with output table FirstRunID whose Output value is 1 and second RunID whose Output value is 0
current code I am using:
select memid, a.runid as aRunid,b.runid as bRunid
into #temp
from FirstTable m inner join
(select min(RunID), MemID [SecondTable] where ouput=1 group by memid)a on m.memid=a.memid
inner join (select RunID, MemID [SecondTable] where ouput=0 )b on m.memid=a.memid and b.runid>a.runid
with cte as
(
select row_number() over(partition by memid, arunid order by brunid ),* from #temp
)
select * from cte where n=1
You can use outer apply operator for this:
select * from t1
outer apply(select top 1 t2.runid from t2
where t1.memid = t2.memid and t2.output = 1 order by t2.runid) as oa1
outer apply(select top 1 t2.runid from t2
where t1.memid = t2.memid and t2.output = 0 order by t2.runid) as oa2
You can do this with conditional aggregation. Based on your results, you don't need the first table:
select t2.memid,
max(case when output = 1 and seqnum = 1 then runid end) as OutputValue1,
max(case when output = 0 and seqnum = 2 then runid end) as OutputValue2
from (select t2.*,
row_number() over (partition by memid, output order by runid) a seqnum
from t2
) t2
group by t2.memid;
declare #FirstTable table
(memid int, name varchar(20))
insert into #firsttable
values
(1,'John'),
(2,'Victor')
declare #secondtable table
(runid int,memid int,output int)
insert into #secondtable
values
(1,1,0),(1,2,1),(2,1,1),(2,2,1),(3,1,1),(3,2,0),(4,1,0),(4,2,0)
;with cte as
(
SELECT *, row_number() over (partition by memid order by runid) seq --sequence
FROM #SECONDTABLE T
where t.output = 1
union all
SELECT *, row_number() over (partition by memid order by runid) seq --sequence
FROM #SECONDTABLE T
where t.output = 0 and
t.runid > (select min(x.runid) from #secondtable x where x.memid = t.memid and x.output = 1 group by x.memid) --lose any O output record where there is no prior 1 output record
)
select cte1.memid,cte1.runid,cte2.runid from cte cte1
join cte cte2 on cte2.memid = cte1.memid and cte2.seq = cte1.seq
where cte1.seq = 1 --remove this test if you want matched pairs
and cte1.output = 1 and cte2.output = 0

PostgreSQL Selecting Most Recent Entry for a Given ID

Table Essentially looks like:
Serial-ID, ID, Date, Data, Data, Data, etc.
There can be Multiple Rows for the Same ID. I'd like to create a view of this table to be used in Reports that only shows the most recent entry for each ID. It should show all of the columns.
Can someone help me with the SQL select? thanks.
There's about 5 different ways to do this, but here's one:
SELECT *
FROM yourTable AS T1
WHERE NOT EXISTS(
SELECT *
FROM yourTable AS T2
WHERE T2.ID = T1.ID AND T2.Date > T1.Date
)
And here's another:
SELECT T1.*
FROM yourTable AS T1
LEFT JOIN yourTable AS T2 ON
(
T2.ID = T1.ID
AND T2.Date > T1.Date
)
WHERE T2.ID IS NULL
One more:
WITH T AS (
SELECT *, ROW_NUMBER() OVER(PARTITION BY ID ORDER BY Date DESC) AS rn
FROM yourTable
)
SELECT * FROM T WHERE rn = 1
Ok, i'm getting carried away, here's the last one I'll post(for now):
WITH T AS (
SELECT ID, MAX(Date) AS latest_date
FROM yourTable
GROUP BY ID
)
SELECT yourTable.*
FROM yourTable
JOIN T ON T.ID = yourTable.ID AND T.latest_date = yourTable.Date
I would use DISTINCT ON
CREATE VIEW your_view AS
SELECT DISTINCT ON (id) *
FROM your_table a
ORDER BY id, date DESC;
This works because distinct on suppresses rows with duplicates of the expression in parentheses. DESC in order by means the one that normally sorts last will be first, and therefor be the one that shows in the result.
https://www.postgresql.org/docs/10/static/sql-select.html#SQL-DISTINCT
This seems like a good use for correlated subqueries:
CREATE VIEW your_view AS
SELECT *
FROM your_table a
WHERE date = (
SELECT MAX(date)
FROM your_table b
WHERE b.id = a.id
)
Your date column would need to uniquely identify each row (like a TIMESTAMP type).

Getting latest data from SQL

I'm having trouble getting the latest data out of this.
I have a table with these data:
ItemId, ShipmentId, Date
Items can be shipped many times and a shipment can contain multiple items.
I need to get the latest shipment for every item.
Table looks like this:
11 12 2011-05-13
11 2 2011-07-01
12 2 2000-03-02
...
The result should be
11 2 2011-07-01
12 2 2000-03-02
I can't find a solution to be exclusive.
How can I get the latest shipment for every item?
Assuming you're working with a database engine that supports ranking functions, use a CTE or subquery to order the results:
;With OrderedItems as (
select ItemId,ShipmentId,Date,
ROW_NUMBER() OVER (PARTITION BY ItemId ORDER By Date desc) as rn
from ItemsTable
)
select * from OrderedItems where rn = 1
select t1.ItemId, t1.ShipmentId, t1.Date
from tab t1
join (
select ItemId, max(Date) as Date
from tab
group by ItemId
) t on t1.ItemId = t.ItemId and t1.Date = t.Date
Didn't test it, but this general idea should work:
SELECT * FROM YOUR_TABLE T1
WHERE
NOT EXISTS (
SELECT * FROM YOUR_TABLE T2
WHERE T1.ItemId = T2.ItemId AND T1.Date < T2.Date
)
In plain English: select rows such that there is no other row with the same ItemId but later Date.
you can use rank() also in CTE
;With Ordered as (
select ItemId,ShipmentId,dates,
rank() OVER ( PARTITION by itemID ORDER By dates desc) as DateRank
from ItemsTable
)
select * from Ordered where DateRank = 1