Adding days to the select query result dates in oracle - date

select from_date_su_rela,to_date_su_rela from RELATION_T;
But the expectation is reducing -56 days from the first row from_date_su_rela and adding the +56 days to last(3rd) row to_date_su_rela. as below
I have written query as ,
select from_date_su_rela-56,to_date_su_rela+56 from RELATION_T; But its adding and reducing days from all the rows as below,
How to make it working as above 2nd image.

One option is to use the row_number analytic function sorting the data both ascending and descending to find the first and last row and then perform the addition and subtraction in a case statement
select case when rn_asc = 1
then from_date_su_rela - 56
else from_date_su_rela
end from_date_su_rela,
case when rn_desc = 1
then to_date_su_rela + 56
else to_date_su_rela
end to_date_su_rela
from (
select from_date_su_rela,
to_date_su_rela,
row_number() over (order by from_date_su_rela desc) rn_desc,
row_number() over (order by from_date_su_rela asc) rn_asc
from relation_t
)

Related

SQL multiple calculation in table creation

I'm trying to create a table with multiple calculation.
I have a base table from which I would like to collect data and insert into the new table. The next columns are calculated based on the base table. So the first few columns are based on the original table, one part of it exactly the same, other part is calculated.
These works fine, however the last 2 columns are not. The calculation of these would be based on the calculated field of the new table.
Can it be solved within one step? Should I use update? As far as I know ranking is not working with that.
INSERT INTO [RAW_NBA_TeamSimpleRating]
(
[Team]
,[Game_total]
,[ORtg_avg]
,[DRtg_avg]
,[ORtg_rank]
,[ORtg_cluster]
)
SELECT
[Team]
,[Game]
,AVG ([ORtg]) OVER (PARTITION BY Team ORDER BY RowNumber rows between 81 preceding and current row) as ORtg_avg
,AVG ([DRtg]) OVER (PARTITION BY Team ORDER BY RowNumber rows between 81 preceding and current row) as DRtg_avg
,RANK () OVER (PARTITION BY [RAW_NBA_TeamSimpleRating].[Game_total] ORDER BY [RAW_NBA_TeamSimpleRating].[ORtg_avg] Desc)
,CASE
WHEN RANK () OVER (PARTITION BY [RAW_NBA_TeamSimpleRating].[Game_total] ORDER BY [RAW_NBA_TeamSimpleRating].[ORtg_avg] DESC) > 10 THEN 'Bottom'
WHEN RANK () OVER (PARTITION BY [RAW_NBA_TeamSimpleRating].[Game_total] ORDER BY [RAW_NBA_TeamSimpleRating].[ORtg_avg] DESC) <= 10 THEN 'TOP'
END
FROM [WRK_NBA_TeamTable]
If you wrap your query you can use the values from the inner select, such as
select Team, Game, ORtg_avg, DRtg_avg, [Rank],
case
when [Rank] > 10 then 'Bottom'
when [Rank] <= 10 then 'TOP'
end as ORtg_cluster
from (
select Team, Game
,Avg (ORtg) over (partition by Team order by RowNumber rows between 81 preceding and current row) as ORtg_avg
,Avg (DRtg) over (partition by Team order by RowNumber rows between 81 preceding and current row) as DRtg_avg
,Rank () over (partition by RAW_NBA_TeamSimpleRating.Game_total order by RAW_NBA_TeamSimpleRating.ORtg_avg desc) as [Rank]
from WRK_NBA_TeamTable
)s

how to concatenate timestamp in different rows in postgresql?

I'm looking for a way to concatenate timestamp in two difference row, for an example, I have this table:
I want it to be grouped by weekday and concatenate the min(start_hour) with max(start_hour), to get something like this
and I'm using this query to retrieve the first image result
The query below should give you what you are looking for provided the information supplied. I made some assumptions. That the '00:00:00' in the start and end hours is not a valid time and can be ignored. If they should be considered valid, then Friday's output would be one entry of '00:00:00' - '11:30:00'.
I created two CTEs, one for the start hours and the other for the end hours where the values are not '00:00:00'. Added a row number to the CTEs so i can match up the day & row_number to get you a set.
SELECT day
,array_to_string(array_agg(t.shift), ',') shifts
FROM (
WITH cte_start AS (
SELECT row_number() OVER (PARTITION BY day)
,day
,start_hour
FROM test22
WHERE start_hour <> '00:00:00'::time
)
,cte_stop AS (
SELECT row_number() OVER (PARTITION BY day)
,day
,stop_hour
FROM test22
WHERE stop_hour <> '00:00:00'::time
)
SELECT cte_start.day
,cte_start.start_hour::varchar || ' - ' || cte_stop.stop_hour::varchar AS shift
FROM cte_start
LEFT OUTER JOIN cte_stop ON cte_start.day = cte_stop.day
AND cte_start.row_number = cte_stop.row_number
) T
GROUP BY T.day
-HTH

TSQL - LEAD for Next Different Row

Is there a way to use the lead function such that I can get the next row where something has changed, as opposed it where it is the same?
In this example, the RowType can be 'in' or 'out', for each 'in' I need to know the next RowNumber where it has become 'out'. I have been playing with the lead function as it is really fast, however I haven't been able to get it working. I just need to do the following really, which is partition by a RowType which isn't the one in the current row.
select
RowNumber
,RowType --In this case I am only interested in RowType = 'In'
, Lead(RowNumber)
OVER (partition by "RowType = out" --This is the bit I am stuck on--
order by RowNumber ASC) as NextOutFlow
from table
order by RowNumber asc
Thanks in advance for any help
Rather than using lead() I would use an outer apply that returns the next row with type out for all rows with type in:
select RowNumber, RowType, nextOut
from your_table t
outer apply (
select min(RowNumber) as nextOut
from your_table
where RowNumber > t.RowNumber and RowType='Out'
) oa
where RowType = 'In'
order by RowNumber asc
Given sample data like:
RowNumber RowType
1 in
2 out
3 in
4 in
5 out
6 in
This would return:
RowNumber RowType nextOut
1 in 2
3 in 5
4 in 5
6 in NULL
I think this will work
If you would use a bit field for in out you would get better performance
;with cte1 as
(
SELECT [inden], [OnOff]
, lag([OnOff]) over (order by [inden]) as [lagOnOff]
FROM [OnOff]
), cte2 as
(
select [inden], [OnOff], [lagOnOff]
, lead([inden]) over (order by [inden]) as [Leadinden]
from cte1
where [OnOff] <> [lagOnOff]
or [lagOnOff] is null
)
select [inden], [OnOff], [lagOnOff], [Leadinden]
from cte2
where [OnOff] = 'true'
probably slower but if you have the right indexes may work
select t1.rowNum as 'rowNumIn', min(t2.rownum) as 'nextRowNumOut'
from tabel t1
join table t2
on t1.rowType = 'In'
and t2.rowType = 'Out'
and t2.rowNum > t1.rowNum
and t2.rowNum < t1.rowNum + 1000 -- if you can constrain it
group by t1.rowNum

multiple extract() with WHERE clause possible?

So far I have come up with the below:
WHERE (extract(month FROM orders)) =
(SELECT min(extract(month from orderdate))
FROM orders)
However, that will consequently return zero to many rows, and in my case, many, because many orders exist within that same earliest (minimum) month, i.e. 4th February, 9th February, 15th Feb, ...
I know that a WHERE clause can contain multiple columns, so why wouldn't the below work?
WHERE (extract(day FROM orderdate)), (extract(month FROM orderdate)) =
(SELECT min(extract(day from orderdate)), min(extract(month FROM orderdate))
FROM orders)
I simply get: SQL Error: ORA-00920: invalid relational operator
Any help would be great, thank you!
Sample data:
02-Feb-2012
14-Feb-2012
22-Dec-2012
09-Feb-2013
18-Jul-2013
01-Jan-2014
Output:
02-Feb-2012
14-Feb-2012
Desired output:
02-Feb-2012
I recreated your table and found out you just messed up the brackets a bit. The following works for me:
where
(extract(day from OrderDate),extract(month from OrderDate))
=
(select
min(extract(day from OrderDate)),
min(extract(month from OrderDate))
from orders
)
Use something like this:
with cte1 as (
select
extract(month from OrderDate) date_month,
extract(day from OrderDate) date_day,
OrderNo
from tablename
), cte2 as (
select min(date_month) min_date_month, min(date_day) min_date_day
from cte1
)
select cte1.*
from cte1
where (date_month, date_day) = (select min_date_month, min_date_day from cte2)
A common table expression enables you to restructure your data and then use this data to do your select. The first cte-block (cte1) selects the month and the day for each of your table rows. Cte2 then selects min(month) and min(date). The last select then combines both ctes to select all rows from cte1 that have the desired month and day.
There is probably a shorter solution to that, however I like common table expressions as they are almost all the time better to understand than the "optimal, shortest" query.
If that is really what you want, as bizarre as it seems, then as a different approach you could forget the extracts and the subquery against the table to get the minimums, and use an analytic approach instead:
select orderdate
from (
select o.*,
row_number() over (order by to_char(orderdate, 'MMDD')) as rn
from orders o
)
where rn = 1;
ORDERDATE
---------
01-JAN-14
The row_number() effectively adds a pseudo-column to every row in your original table, based on the month and day in the order date. The rn values are unique, so there will be one row marked as 1, which will be from the earliest day in the earliest month. If you have multiple orders with the same day/month, say 01-Jan-2013 and 01-Jan-2014, then you'll still only get exactly one with rn = 1, but which is picked is indeterminate. You'd need to add further order by conditions to make it deterministic, but I have no idea what you might want.
That is done in the inner query; the outer query then filters so that only the records marked with rn = 1 is returned; so you get exactly one row back from the overall query.
This also avoids the situation where the earliest day number is not in the earliest month number - say if you only had 01-Jan-2014 and 02-Feb-2014; comparing the day and month separately would look for 01-Feb-2014, which doesn't exist.
SQL Fiddle (with Thomas Tschernich's anwer thrown in too, giving the same result for this data).
To join the result against your invoice table, you don't need to join to the orders table again - especially not with a cross join, which is skewing your results. You can do the join (at least) two ways:
SELECT
o.orderno,
to_char(o.orderdate, 'DD-MM-YYYY'),
i.invno
FROM
(
SELECT o.*,
row_number() over (order by to_char(orderdate, 'MMDD')) as rn
FROM orders o
) o, invoices i
WHERE i.invno = o.invno
AND rn = 1;
Or:
SELECT
o.orderno,
to_char(o.orderdate, 'DD-MM-YYYY'),
i.invno
FROM
(
SELECT orderno, orderdate, invno
FROM
(
SELECT o.*,
row_number() over (order by to_char(orderdate, 'MMDD')) as rn
FROM orders o
)
WHERE rn = 1
) o, invoices i
WHERE i.invno = o.invno;
The first looks like it does more work but the execution plans are the same.
SQL Fiddle with your pastebin-supplied query that gets two rows back, and these two that get one.

PostgreSQL: getting ordinal rank (row index? ) efficiently

You have a table like so:
id dollars dollars_rank points points_rank
1 20 1 35 1
2 18 2 30 3
3 10 3 33 2
I want a query that updates the table's rank columns (dollars_rank and points_rank) to set the rank for the given ID, which is just the row's index for that ID sorted by the relevant column in a descending order. How best to do this in PostgreSQL?
The window function dense_rank() is what you need - or maybe rank(). The UPDATE could look like this:
UPDATE tbl
SET dollars_rank = r.d_rnk
, points_rank = r.p_rnk
FROM (
SELECT id
, dense_rank() OVER (ORDER BY dollars DESC NULLS LAST) AS d_rnk
, dense_rank() OVER (ORDER BY points DESC NULLS LAST) AS p_rnk
FROM tbl
) r
WHERE tbl.id = r.id;
fiddle
NULLS LAST is only relevant if the involved columns can be NULL:
Sort by column ASC, but NULL values first?