I have a requirement to show most recent records when user selects the option to view most recent records. I have 3 different tables from which I take data and display on the screen.
Below are the sample tables created.
Create table one(sealID integer,product_ser_num varchar2(20),create_time timestamp,status varchar2(10));
create table two(transID integer,formatID integer, formatStatus varchar,ctimeStamp timestamp,sealID integer);
create table three(transID integer,fieldStatus varchar,fieldValue varchar,exctype varchar);
I'm joining above 3 tables and showing the results in a single screen. I want to display the most recent records based on the timestamp.
Please find the sample data on the screen taken from 3 different tables.
ProductSerialNumber formatID formatStatus fieldStatus TimeStamp
ASD100 100 P P 2015-09-03 10:30:22
ASD100 200 p P 2015-09-03 10:30:22
ASD100 100 p P 2015-09-03 10:22:11
ASD100 200 p P 2015-09-03 10:22:11
I want to display the most recent records from the above shown table which should return first 2 rows as they are the recent records when checked with the timestamp column.
Please suggest what changes to be done to the below query to show most recent records.
SELECT transId,product_ser_num,status, to_char(timestamp, 'yyyy-mm-dd hh24:mi:ss') timestamp,
cnt
FROM (SELECT one.*,
row_number() over(ORDER BY
CASE
WHEN :orderDirection like '%asc%' THEN
CASE
WHEN :orderBy='product_ser_num' THEN product_ser_num,
WHEN :orderBy='status' THEN status
WHEN :orderBy='timestamp' THEN to_char(timestamp, 'yyyy-mm-dd hh24:mi:ss')
ELSE to_char(timestamp, 'yyyy-mm-dd hh24:mi:ss')
END
END ASC,
CASE
WHEN :orderDirection like '%desc%' THEN
CASE
WHEN :orderBy='product_ser_num' THEN product_ser_num,
WHEN :orderBy='status' THEN status
WHEN :orderBy='timestamp' THEN to_char(timestamp, 'yyyy-mm-dd hh24:mi:ss')
ELSE to_char(timestamp, 'yyyy-mm-dd hh24:mi:ss')
END
END DESC , transId ASC) line_number
FROM (select one_inner.*, COUNT(1) OVER() cnt
from (select two_tran.transaction_id,
one_res.product_serial_number productSerialNumber,
one_res.status status,from one one_res
left outer join two two_trans on two_trans.sealID = one_res.sealID
left outer join three three_flds on two_tran.transID = three_flds.transID and (three_flds.fieldStatus = 'P')
I don't think you are looking for a Top-n query as your topic title suggests.
It seems like you want to display the data in a customized order, as you have shown in the first image. You want the set of three rows to be grouped together on the basis of timestamp.
I have prepared a small test case to demonstrate the custom order of the rows:
SQL> WITH DATA(ID, num, datetime) AS(
2 SELECT 10, 1001, SYSDATE FROM dual UNION ALL
3 SELECT 10, 6009, SYSDATE FROM dual UNION ALL
4 SELECT 10, 3951, SYSDATE FROM dual UNION ALL
5 SELECT 10, 1001, SYSDATE -1 FROM dual UNION ALL
6 SELECT 10, 6009, SYSDATE -1 FROM dual UNION ALL
7 SELECT 10, 3951, SYSDATE -1 FROM dual
8 )
9 SELECT ID,
10 num,
11 TO_CHAR(DATETIME, 'yyyy-mm-dd hh24:mi:ss') TIMESTAMP
12 FROM
13 (SELECT t.*,
14 row_number() OVER(ORDER BY DATETIME DESC,
15 CASE num
16 WHEN 1001
17 THEN 1
18 WHEN 6009
19 THEN 2
20 WHEN 3951
21 THEN 3
22 END, num) rn
23 FROM DATA t
24 );
ID NUM TIMESTAMP
---------- ---------- -------------------
10 1001 2015-09-04 11:04:48
10 6009 2015-09-04 11:04:48
10 3951 2015-09-04 11:04:48
10 1001 2015-09-03 11:04:48
10 6009 2015-09-03 11:04:48
10 3951 2015-09-03 11:04:48
6 rows selected.
Now, you can see that for the same ID 10, the NUM values are grouped and also in a custom order.
This query seems very large and complex, so this may be oversimplifying things:
Add a clause to the end limit 3 ?
What I think you need to do is:
select
max(timestamp), engine_serial_number, formatID
from
<
joins here
>
group by engine_serial_number, formatID
This will basically give you the lines you want, but not all metadata.
Hence, you will just have to re-join all this with the main join to get the rest of the info (join on all three columns, engine serial number, formatID AND timestamp).
That should work.
Hope this helps!
It's hard to give you a precise answer, because your query is incomplete. But I'll give you the general idea, and you can tweak it into your query.
One way to accomplish what you want is by using the dense_rank() analytical function to number your rows by timestamp in descending order (You could use rank() too in this case, it doesn't actually matter). All rows with the same timestamp will be assigned the same "rank", so you can then filter by rank to only get the most recent records.
Try to adjust your query to something like this:
select ...
from (select ...,
dense_rank() over (order by timestamp desc) as timestamp_rank
from ...)
where timestamp_rank = 1
...
I suspect that with a better understanding of your data model and query, there would probably be a better solution. But based on the information provided, I think that the above should yield the results you are looking for.
Related
I have a set of data that I am passing a user input of date to pull from a table.
What I am trying to do is setup up logic as such:
Here is the original table:
User DATE_TIME Value
HH1 5/20/2018 1:00 50
HH1 5/20/2018 10:00 50
HH1 5/20/2018 18:00 120
HH1 5/25/2018 12:00 10
HH1 5/26/2018 10:00 15
User passes 05/20/2018 into the sql query for DATE_TIME
The output is as follows:
User DATE_TIME Value
HH1 5/20/2018 1:00 50
HH1 5/20/2018 10:00 50
HH1 5/20/2018 18:00 120
Now the user passes 05/21/2018 into DATE_TIME
Result is nothing
What I am trying to accomplish is...if there are no results on the current day, then pull the latest value in the database, in this case:
User DATE_TIME Value
HH1 5/20/2018 18:00 120
I am not sure how to find this most recent value.
Any help would be appreciated, thanks!
In my solution below, I named the base table TBL and I changed the name of the first column to USR since "user" is an Oracle keyword.
Most of the work is done in the subquery. Look at the FROM clause first: I cross-join to a small subquery that creates an actual date from the input (assumed to be given as a string in MM/DD/YYYY format). Depending on your application, you may be able to input a date directly and not have to convert to date in the query. One way or another, you should be able to use the input date.
The WHERE clause limits the rows to dates up to the input date given (with that date included). Then we rank the rows in descending order, but with a modification we make first: If there are any rows on the input date, their time-of-day component is truncated to zero. (The DATE_TIME value is replaced with the input date for those rows only.) So if there are any rows for the input date, all the rows for that date will get rank = 1, and all other rows will get higher ranks. However, if there are no rows for the input date, then the most recent row before that date will get rank = 1 (and only that row, assuming there are no duplicates in the DATE_TIME column).
So then the job of the outer query is easy: keep only the row OR ROWS where rank = 1. This means either ALL the rows for the input date (if there were any), or the single most recent row before that date.
The subquery-outer query structure cannot be avoided, because the WHERE clause is evaluated before the ranks can be calculated in the same query. If we want the WHERE clause to reference ranks, it must be in an outer query (at a higher level than the query that generates the ranks).
The query should be efficient, since the optimizer is smart enough to see we only want rank = 1 in the outer query, so it will not actually compute ALL the ranks (it will not fully order the subquery rows by DATE_TIME). And, of course, if there is an index on DATE_TIME, it will be used.
You didn't say anything about the role played by USR. If it plays no role, then the solution should work as-is. If you also input a USR, then add the filter in the WHERE clause of the subquery. If you need a result for each USR separately, for the same input date, add PARTITION BY USR in the analytic clause of the RANK() function.
select usr, date_time, value
from (
select usr, date_time, value,
rank() over (order by case when date_time >= input_date then input_date
else date_time end desc) as rnk
from tbl cross join
(select to_date(:input_date, 'mm/dd/yyyy') as input_date from dual)
where date_time < input_date + 1
)
where rnk = 1
;
Below query will make use of window function ROW_NUMBER PARTITION BY ORDER BY. It will combine (UNION ALL) the records equal to input date OR if not found, get the rec with max date. Rownum =1 and rank=1 will only get the first record. If date is same with user input then the max date will be at the bottom and will not be selected. If date is not found then record with max date is the only record, thus will be shown. See demo at http://sqlfiddle.com/#!4/931c2/1.
SELECT usr as "user", date_time, valu as "value"
FROM (
SELECT usr, date_time, valu,
row_number() over (partition by trunc(date_time)
order by date_time desc) as rnk
FROM TEST
WHERE to_char(DATE_TIME, 'yyyy-mm-dd') = '2018-05-21'
UNION ALL
SELECT usr, date_time, valu,
row_number() over (order by date_time desc) as rnk
FROM TEST) t
WHERE rnk = 1
AND rownum = 1;
Ugly as hell, who knows how it'll behave on large tables (probably not very well), but - kind of works on a simple case (based on Oracle).
SQL> create table test (usern varchar2(3), date_time date, hour number, value number);
Table created.
SQL> insert into test
2 (select 'HH1', date '2018-05-20', 1, 50 from dual union
3 select 'HH1', date '2018-05-20', 10, 50 from dual union
4 select 'HH1', date '2018-05-20', 18, 120 from dual union
5 select 'HH1', date '2018-05-25', 12, 10 from dual union
6 select 'HH1', date '2018-05-26', 10, 15 from dual
7 );
5 rows created.
SQL>
Testing:
SQL> select *
2 from test t
3 where t.date_time = (select max(t1.date_time) from test t1
4 where t1.usern = t.usern
5 and t1.date_time <= to_date('&&date_you_enter', 'dd.mm.yyyy')
6 )
7 and t.hour = (select max(case when e.it_exists = 'Y' then t.hour
8 else t2.hour
9 end)
10 from test t2 left join
11 (select t3.usern, 'Y' it_exists
12 from test t3
13 where t3.date_time = to_date('&&date_you_enter', 'dd.mm.yyyy')
14 ) e on e.usern = t2.usern
15 )
16 order by t.date_time, t.hour;
Enter value for date_you_enter: 20.05.2018
USE DATE_TIME HOUR VALUE
--- ---------- ---------- ----------
HH1 20.05.2018 1 50
HH1 20.05.2018 10 50
HH1 20.05.2018 18 120
SQL> undefine date_you_enter
SQL> /
Enter value for date_you_enter: 21.05.2018
USE DATE_TIME HOUR VALUE
--- ---------- ---------- ----------
HH1 20.05.2018 18 120
SQL> undefine date_you_enter
SQL> /
Enter value for date_you_enter: 25.05.2018
USE DATE_TIME HOUR VALUE
--- ---------- ---------- ----------
HH1 25.05.2018 12 10
SQL>
It will be helpful if you post your SQL Statement. But try this
Select
column1, column2,
FROM
YourTable
Where Date <= #dateInputByUserGoesHere
EDIT: added third requirement after playing with solution from Tim Biegeleisen
EDIT2: modified Robbie's DOB to be before his parent's marriage date
I am trying to create a query that will look at two tables and determine the difference in dates based on a percentage. I know, super confusing... Let me try and explain using the tables below:
Bob and Mary are married on 2010-01-01 and expect 4 kids (Parent table)
I want to know how many years it took until they met 50% of their expected kids (i.e. 2/4 kids). Using the Child table to see the DOB of their 4 kids, we know that Frankie is the second child which meets our 50% threshold so we use Frankie's DOB and subtract it from Frankie's parent's marriage date and end up with 3 years!
If the goal isn't reached then display no value e.g. Mick and Jo only had 1 child so far so they haven't yet reached their goal
Hoping this is doable using BigQuery standard SQL.
Parent table
id married_couple married_at expected_kids
--------------------------------------
1 Bob and Mary 2010-01-01 4
2 Mick and Jo 2010-01-01 4
Child table
id child_name parent_id date_of_birth
--------------------------------------
1 Eddie 1 2012-01-01
2 Frankie 1 2013-01-01
3 Robbie 1 2005-01-01
4 Duncan 1 2015-01-01
5 Rick 2 2014-01-01
Expected SQL result
parent_id half_goal_reached(years)
--------------------------------------
1 3
2
Below both soluthions for BigQuery Standard SQL
First one is more in classic sql way, the second one is more of BigQuery style (I think)
First Solution: with analytics function
#standardSQL
SELECT
parent_id,
IF(
MAX(pos) = MAX(CAST(expected_kids / 2 AS INT64)),
MAX(DATE_DIFF(date_of_birth, married_at, YEAR)),
NULL
) AS half_goal_reached
FROM (
SELECT c.parent_id, c.date_of_birth, expected_kids, married_at,
ROW_NUMBER() OVER(PARTITION BY c.parent_id ORDER BY c.date_of_birth) AS pos
FROM `child` AS c
JOIN `parent` AS p
ON c.parent_id = p.id
)
WHERE pos <= CAST(expected_kids / 2 AS INT64)
GROUP BY parent_id
Second Solution: with use of ARRAY
#standardSQL
SELECT
parent_id,
DATE_DIFF(dates[SAFE_ORDINAL(CAST(expected_kids / 2 AS INT64))], married_at, YEAR) AS half_goal_reached
FROM (
SELECT
parent_id,
ARRAY_AGG(date_of_birth ORDER BY date_of_birth) AS dates,
MAX(expected_kids) AS expected_kids,
MAX(married_at) AS married_at
FROM `child` AS c
JOIN `parent` AS p
ON c.parent_id = p.id
GROUP BY parent_id
)
Dummy Data
You can test / play with both solutions using below dummy data
#standardSQL
WITH `parent` AS (
SELECT 1 id, 'Bob and Mary' married_couple, DATE '2010-01-01' married_at, 4 expected_kids UNION ALL
SELECT 2, 'Mick and Jo', DATE '2010-01-01', 4
),
`child` AS (
SELECT 1 id, 'Eddie' child_name, 1 parent_id, DATE '2012-01-01' date_of_birth UNION ALL
SELECT 2, 'Frankie', 1, DATE '2013-01-01' UNION ALL
SELECT 3, 'Robbie', 1, DATE '2014-01-01' UNION ALL
SELECT 4, 'Duncan', 1, DATE '2015-01-01' UNION ALL
SELECT 5, 'Rick', 2, DATE '2014-01-01'
)
Try the following query, whose logic is too verbose to explain it well. I join the parent and child tables, bringing into line the parent id, number of years elapsed since marriage, running number of children, and expected number of children. With this information in hand, we can easily find the first row whose running number of children matches or exceeds half of the expected number.
SELECT parent_id, num_years AS half_goal_reached
FROM
(
SELECT parent_id, num_years, cnt, expected_kids,
ROW_NUMBER() OVER (PARTITION BY id ORDER BY num_years) rn
FROM
(
SELECT
t2.parent_id,
YEAR(t2.date_of_birth) - YEAR(t1.married_at) AS num_years,
(SELECT COUNT(*) FROM child c
WHERE c.parent_id = t2.parent_id AND
c.date_of_birth <= t2.date_of_birth) AS cnt,
t1.expected_kids
FROM parent t1
INNER JOIN child t2
ON t1.id = t2.parent_id
) t
WHERE
cnt >= expected_kids / 2
) t
WHERE t.rn = 1;
Note that there may be issues with how I computed the yearly differences, or how I compute the threshhold for half the number of expected children. Also, if we were using a recent enterprise database we could have used an analytic function to get the running number of children instead of a correlated subquery, but I was unsure if Big Query would support that, so I used the latter.
l want to get the gap between dates range via SQL query lets see the situation:
l have table employees like : Every month the employee deserve payment
ID Name From_date To_date Paid_Amount`
1 ali 01/01/2002 31/01/2002 300
2 ali 01/02/2002 28/02/2002 300
3 ali 01/04/2002 30/04/2002 300
4 ali 01/05/2002 31/05/2002 300
5 ali 01/07/2002 31/07/2002 300
Now, we notice there are no payments in March and June
so, how by SQL query I can't get these months ??
Try this,
with mine(ID,Name,From_date,To_date,Paid_Amount) as
(
select 1,'ali','01/01/2002','31/01/2002',300 from dual union all
select 2,'ali','01/02/2002','28/02/2002',300 from dual union all
select 3,'ali','01/04/2002','30/04/2002',300 from dual union all
select 4,'ali','01/05/2002','31/05/2002',300 from dual union all
select 5,'ali','01/07/2002','31/07/2002',300 from dual
),
gtfirst (fromdt,todt) as (
select min(to_Date(from_Date,'dd/mm/yyyy')) fromdt,max(to_Date(to_Date,'dd/mm/yyyy')) todt from mine
),
dualtbl(first,last,fromdt,todt) as
(
select * from(select TRUNC(ADD_MONTHS(fromdt, rownum-1), 'MM') AS first,TRUNC(LAST_DAY(ADD_MONTHS(fromdt, rownum-1))) AS last,fromdt,todt from gtfirst connect by level <=12)
where first between fromdt and todt and last between fromdt and todt
)
select to_char(first,'month') no_payment_date from dualtbl where first not in (select to_Date(from_Date,'dd/mm/yyyy') from mine)
and first not in (select to_Date(to_date,'dd/mm/yyyy') from mine)
If you want to get the date difference between one payment date and the previous payment date and the ID field is sequential, then you may simply join back to the table and select the previous row.
SELECT X.From_date, Y.From_date, Y.From_date - X.From_date Difference
FROM Employees X
LEFT OUTER JOIN Employees Y ON Y.ID = X.ID - 1
If the ID field is not sequential, then you can use a similar method, but build a temporary table with a row index that you can use to join back to the previous payment.
I'm struggling to find the query for the following task
I have the following data and want to find the total network day for each unique ID
ID From To NetworkDay
1 03-Sep-12 07-Sep-12 5
1 03-Sep-12 04-Sep-12 2
1 05-Sep-12 06-Sep-12 2
1 06-Sep-12 12-Sep-12 5
1 31-Aug-12 04-Sep-12 3
2 04-Sep-12 06-Sep-12 3
2 11-Sep-12 13-Sep-12 3
2 05-Sep-12 08-Sep-12 3
Problem is the date range can be overlapping and I can't come up with SQL that will give me the following results
ID From To NetworkDay
1 31-Aug-12 12-Sep-12 9
2 04-Sep-12 08-Sep-12 4
2 11-Sep-12 13-Sep-12 3
and then
ID Total Network Day
1 9
2 7
In case the network day calculation is not possible just get to the second table would be sufficient.
Hope my question is clear
We can use Oracle Analytics, namely the "OVER ... PARTITION BY" clause, in Oracle to do this. The PARTITION BY clause is kind of like a GROUP BY but without the aggregation part. That means we can group rows together (i.e. partition them) and them perform an operation on them as separate groups. As we operate on each row we can then access the columns of the previous row above. This is the feature PARTITION BY gives us. (PARTITION BY is not related to partitioning of a table for performance.)
So then how do we output the non-overlapping dates? We first order the query based on the (ID,DFROM) fields, then we use the ID field to make our partitions (row groups). We then test the previous row's TO value and the current rows FROM value for overlap using an expression like: (in pseudo code)
max(previous.DTO, current.DFROM) as DFROM
This basic expression will return the original DFROM value if it doesnt overlap, but will return the previous TO value if there is overlap. Since our rows are ordered we only need to be concerned with the last row. In cases where a previous row completely overlaps the current row we want the row then to have a 'zero' date range. So we do the same thing for the DTO field to get:
max(previous.DTO, current.DFROM) as DFROM, max(previous.DTO, current.DTO) as DTO
Once we have generated the new results set with the adjusted DFROM and DTO values, we can aggregate them up and count the range intervals of DFROM and DTO.
Be aware that most date calculations in database are not inclusive such as your data is. So something like DATEDIFF(dto,dfrom) will not include the day dto actually refers to, so we will want to adjust dto up a day first.
I dont have access to an Oracle server anymore but I know this is possible with the Oracle Analytics. The query should go something like this:
(Please update my post if you get this to work.)
SELECT id,
max(dfrom, LAST_VALUE(dto) OVER (PARTITION BY id ORDER BY dfrom) ) as dfrom,
max(dto, LAST_VALUE(dto) OVER (PARTITION BY id ORDER BY dfrom) ) as dto
from (
select id, dfrom, dto+1 as dto from my_sample -- adjust the table so that dto becomes non-inclusive
order by id, dfrom
) sample;
The secret here is the LAST_VALUE(dto) OVER (PARTITION BY id ORDER BY dfrom) expression which returns the value previous to the current row.
So this query should output new dfrom/dto values which dont overlap. It's then a simple matter of sub-querying this doing (dto-dfrom) and sum the totals.
Using MySQL
I did haves access to a mysql server so I did get it working there. MySQL doesnt have results partitioning (Analytics) like Oracle so we have to use result set variables. This means we use #var:=xxx type expressions to remember the last date value and adjust the dfrom/dto according. Same algorithm just a little longer and more complex syntax. We also have to forget the last date value any time the ID field changes!
So here is the sample table (same values you have):
create table sample(id int, dfrom date, dto date, networkDay int);
insert into sample values
(1,'2012-09-03','2012-09-07',5),
(1,'2012-09-03','2012-09-04',2),
(1,'2012-09-05','2012-09-06',2),
(1,'2012-09-06','2012-09-12',5),
(1,'2012-08-31','2012-09-04',3),
(2,'2012-09-04','2012-09-06',3),
(2,'2012-09-11','2012-09-13',3),
(2,'2012-09-05','2012-09-08',3);
On to the query, we output the un-grouped result set like above:
The variable #ld is "last date", and the variable #lid is "last id". Anytime #lid changes, we reset #ld to null. FYI In mysql the := operators is where the assignment happens, an = operator is just equals.
This is a 3 level query, but it could be reduced to 2. I went with an extra outer query to keep things more readable. The inner most query is simple and it adjusts the dto column to be non-inclusive and does the proper row ordering. The middle query does the adjustment of the dfrom/dto values to make them non-overlapped. The outer query simple drops the non-used fields, and calculate the interval range.
set #ldt=null, #lid=null;
select id, no_dfrom as dfrom, no_dto as dto, datediff(no_dto, no_dfrom) as days from (
select if(#lid=id,#ldt,#ldt:=null) as last, dfrom, dto, if(#ldt>=dfrom,#ldt,dfrom) as no_dfrom, if(#ldt>=dto,#ldt,dto) as no_dto, #ldt:=if(#ldt>=dto,#ldt,dto), #lid:=id as id,
datediff(dto, dfrom) as overlapped_days
from (select id, dfrom, dto + INTERVAL 1 DAY as dto from sample order by id, dfrom) as sample
) as nonoverlapped
order by id, dfrom;
The above query gives the results (notice dfrom/dto are non-overlapping here):
+------+------------+------------+------+
| id | dfrom | dto | days |
+------+------------+------------+------+
| 1 | 2012-08-31 | 2012-09-05 | 5 |
| 1 | 2012-09-05 | 2012-09-08 | 3 |
| 1 | 2012-09-08 | 2012-09-08 | 0 |
| 1 | 2012-09-08 | 2012-09-08 | 0 |
| 1 | 2012-09-08 | 2012-09-13 | 5 |
| 2 | 2012-09-04 | 2012-09-07 | 3 |
| 2 | 2012-09-07 | 2012-09-09 | 2 |
| 2 | 2012-09-11 | 2012-09-14 | 3 |
+------+------------+------------+------+
How about constructing an SQL which merges intervals by removing holes and considering only maximum intervals. It goes like this (not tested):
SELECT DISTINCT F.ID, F.From, L.To
FROM Temp AS F, Temp AS L
WHERE F.From < L.To AND F.ID = L.ID
AND NOT EXISTS (SELECT *
FROM Temp AS T
WHERE T.ID = F.ID
AND F.From < T.From AND T.From < L.To
AND NOT EXISTS ( SELECT *
FROM Temp AS T1
WHERE T1.ID = F.ID
AND T1.From < T.From
AND T.From <= T1.To)
)
AND NOT EXISTS (SELECT *
FROM Temp AS T2
WHERE T2.ID = F.ID
AND (
(T2.From < F.From AND F.From <= T2.To)
OR (T2.From < L.To AND L.To < T2.To)
)
)
with t_data as (
select 1 as id,
to_date('03-sep-12','dd-mon-yy') as start_date,
to_date('07-sep-12','dd-mon-yy') as end_date from dual
union all
select 1,
to_date('03-sep-12','dd-mon-yy'),
to_date('04-sep-12','dd-mon-yy') from dual
union all
select 1,
to_date('05-sep-12','dd-mon-yy'),
to_date('06-sep-12','dd-mon-yy') from dual
union all
select 1,
to_date('06-sep-12','dd-mon-yy'),
to_date('12-sep-12','dd-mon-yy') from dual
union all
select 1,
to_date('31-aug-12','dd-mon-yy'),
to_date('04-sep-12','dd-mon-yy') from dual
union all
select 2,
to_date('04-sep-12','dd-mon-yy'),
to_date('06-sep-12','dd-mon-yy') from dual
union all
select 2,
to_date('11-sep-12','dd-mon-yy'),
to_date('13-sep-12','dd-mon-yy') from dual
union all
select 2,
to_date('05-sep-12','dd-mon-yy'),
to_date('08-sep-12','dd-mon-yy') from dual
),
t_holidays as (
select to_date('01-jan-12','dd-mon-yy') as holiday
from dual
),
t_data_rn as (
select rownum as rn, t_data.* from t_data
),
t_model as (
select distinct id,
start_date
from t_data_rn
model
partition by (rn, id)
dimension by (0 as i)
measures(start_date, end_date)
rules
( start_date[for i
from 1
to end_date[0]-start_date[0]
increment 1] = start_date[0] + cv(i),
end_date[any] = start_date[cv()] + 1
)
order by 1,2
),
t_network_days as (
select t_model.*,
case when
mod(to_char(start_date, 'j'), 7) + 1 in (6, 7)
or t_holidays.holiday is not null
then 0 else 1
end as working_day
from t_model
left outer join t_holidays
on t_holidays.holiday = t_model.start_date
)
select id,
sum(working_day) as network_days
from t_network_days
group by id;
t_data - your initial data
t_holidays - contains list of holidays
t_data_rn - just adds unique key (rownum) to each row of t_data
t_model - expands t_data date ranges into a flat list of dates
t_network_days - marks each date from t_model as working day or weekend based on day of week (Sat and Sun) and holidays list
final query - calculates number of network day per each group.
I have a table that looks like this,
Date Value
01/01/2010 03:59:00 324.44
01/02/2010 09:31:00 NULL
01/02/2010 09:32:00 NULL
.
.
.
01/02/2010 11:42:00 NULL
I want the first valid value to appear in all following rows. This is what I did,
select date,
nvl(value, LAST_VALUE(value IGNORE NULLS) over (order by value RANGE BETWEEN 1 PRECEDING AND CURRENT ROW)) value
from
table
This shows no difference at all, but if I say RANGE BETWEEN 3 PRECEDING AND CURRENT ROW it copies the data to all the rows. I'm not clear why this is happening. Can anyone explain if I'm misunderstanding how to use preceding?
Analytic functions still work on sets of data. They do not process one row at a time, you would need PL/SQL or MODEL to do that. PRECEDING refers to the last X rows, but before the analytic function has been applied.
These problems can be confusing in SQL because you have to build the logic into defining the set, instead of trying to pass data from one row to another. That's why I used CASE with LAST_VALUE in my previous answer.
Edit:
I've added a simple data set so we can all run the exact same query. VALUE1 seems to work to me, am I missing something? Part of the problem with VALUE2 is that the analytic ORDER BY uses VALUE, instead of the date.
select id, the_date, value
,last_value(value ignore nulls) over
(partition by id order by the_date) value1
,nvl(value, LAST_VALUE(value IGNORE NULLS) over
(order by value RANGE BETWEEN 1 PRECEDING AND CURRENT ROW)) value2
from
(
select 1 id, date '2011-01-01' the_date, 100 value from dual union all
select 1 id, date '2011-01-02' the_date, null value from dual union all
select 1 id, date '2011-01-03' the_date, null value from dual union all
select 1 id, date '2011-01-04' the_date, null value from dual union all
select 1 id, date '2011-01-05' the_date, 200 value from dual
)
order by the_date;
Results:
ID THE_DATE VALUE VALUE1 VALUE2
1 1/1/2011 100 100 100
1 1/2/2011 100
1 1/3/2011 100
1 1/4/2011 100
1 1/5/2011 200 200 200
It is possible to copy one row at time because i had done that using java Logic and Sql query
Statement sta;
ResultSet rs,rslast;
try{
//Connection creation code and "con" is an object of Connection Class so don't confuse about that.
sta = con.createStatement();
rs=sta.executeQuery("SELECT * FROM TABLE NAME");
rslast=sta.executeQuery("SELECT * FROM TABLENAME WHERE ID = (SELECT MAX(ID) FROM TABLENAME)");
rslast.next();
String digit =rslast.getString("ID");
System.out.print("ID"+rslast.getString("ID")); // it gives ID of the Last Record.
Instead using this method u can also use ORDER by Date in Descending order.
Now i hope u make logic that only insert Last record.