How to get the max date per month using SQL - max

Something went wrong and I realise that I do not get what I want. I have the following lines in a table:
0000527746 1000 10.06.2017 20170718100757.5010080
0000527746 1000 10.06.2017 20170718100757.5039300
0000527746 1000 11.06.2017 20170718100839.9209480
0000527746 1000 11.06.2017 20170718100906.3337170
0000527746 1000 24.07.2017 20170718095843.3555610
0000527746 1000 24.07.2017 20170718100209.2203570
0000527746 1000 24.07.2017 20170718100757.4970390
and I want to select the last date of each month namely I want the select to bring me the following lines
0000527746 1000 11.06.2017 20170718100906.3337170
0000527746 1000 24.07.2017 20170718100757.4970390
I use the following sql
select bukrs kunnr dat max( time ) as time
from zcollectoraction into corresponding fields of table it_collectoraction
where bukrs = p_bukrs and
kunnr in so_kunnr and
dat in so_date
group by bukrs kunnr dat.
but it displays the following lines
0000527746 1000 11.06.2017 20170718100906.3337170
0000527746 1000 11.06.2017 20170718100906.3337170
0000527746 1000 24.07.2017 20170718100757.4970390
What to do in order to have 1 line per month?

I think there are two solutions for this matter.
1) You can add yearmonth field to your db table. And add this field to group by statement.
0000527746 1000 10.06.2017 20170718100757.5010080 201706
0000527746 1000 10.06.2017 20170718100757.5039300 201706
0000527746 1000 11.06.2017 20170718100839.9209480 201706
0000527746 1000 11.06.2017 20170718100906.3337170 201706
0000527746 1000 24.07.2017 20170718095843.3555610 201707
0000527746 1000 24.07.2017 20170718100209.2203570 201707
0000527746 1000 24.07.2017 20170718100757.4970390 201707
select bukrs kunnr dat max( time ) as time
from zcollectoraction into corresponding fields of table
it_collectoraction
where bukrs = p_bukrs and
kunnr in so_kunnr and
dat in so_date
group by bukrs kunnr dat yearmonth.
2) You can select all the data and arrange in loop statement. Or you can use your old select query does not matter at all.
select bukrs kunnr dat time
from zcollectoraction into corresponding fields of table
it_collectoraction
where bukrs = p_bukrs and
kunnr in so_kunnr and
dat in so_date .
loop at it_collectoraction into data(ls_coll).
delete it_collectoraction[] WHERE dat(6) = ls_coll-dat(6)
and dat < = ls_coll-dat
and time < ls_coll-time.
endloop.

What you need is to group by not dat, but by month and year - this clause will work:
GROUP BY bukrs, kunnr, MONTH(dat), YEAR(dat)

Hello and thanks for your answers.
I solved the problem by doing 2 selects.
In the 1st I get the last day or days of the month with the following selection
select bukrs kunnr yearmonth max( dat ) as dat
from zcollectoraction into corresponding fields of table it_collectoraction
where bukrs = p_bukrs and
kunnr in so_kunnr and
dat in so_date
group by bukrs kunnr yearmonth.
and then I made a loop to the internal table to fill the remaining data and select the MAX Time for all records and especially when there are more than 1 lines per bukrs, kunnr and date.
select single * from zcollectoraction
into corresponding fields of wa_collectoraction
where bukrs = wa_collectoraction-bukrs and
kunnr = wa_collectoraction-kunnr and
dat = wa_collectoraction-dat and
time = ( select max( time ) as time
from zcollectoraction
where bukrs = wa_collectoraction-bukrs and
kunnr = wa_collectoraction-kunnr and
dat = wa_collectoraction-dat ).
Again thanks
Elias

Related

Does "OFFSET" in Oracle reads all data from first?

I know that OFFSET 10000 LIMIT 20 in Mysql reads all data from first to 10020, and discards first 10000 data.
Does OFFSET 10000 ROWS FETCH NEXT 20 ROWS ONLY in Oracle work the same?
No, but of course it must read the proportionate of rows to satisfy the ordering sequence,
eg
select * from emp
order by empno
reads every row so they can be sorted, and
select * from emp
where deptno = 10
order by empno
reads every row for department of 10 and then can sort them.
(There's a few special cases where we can skip that with indexes etc, but that's a separate discussion).
Coming back to FETCH FIRST / OFFSET, in effect we are amending the query to satisfy that need:
select ...
from mytable
order by col
offset 100 fetch 20
becomes
select ..., row_number() over ( order by col) as r
from mytable
because then we can do
select
from ( select ..., row_number() over ( order by col) as r
from mytable
)
where r between 100 and 120
to match your FETCH/OFFSET needs.

Oracle query to obtain batches of rows

So here is my problem: I need to get batches of rows (select statements) for a migration to another database (other then oracle).
Suggested solution: I take batches of rows (using rowid maybe?) example:
batch1: 0-10000,
batch2: 10000 - 20000,
batchn: 10000(n) - 10000(n+1)
So what should my query be?
batch1: select * from table_name where rownum >= 0 and rownum < 10000,
batch2: select * from table_name where rownum >= 10000 and rownum < 20000,
batch n: select * from table_name where rownum >= 10000*n and rownum < 10000*(n+1)
This does not work, (only the first select will work).
PS, I am pulling this data from a nodejs app, and thus I am sending in these batch queries in a for loop.
To illustrate my comment:
-- Between rows --
SELECT * FROM
( SELECT deptno, ename, sal, ROW_NUMBER() OVER (ORDER BY ename) Row_Num
FROM scott.emp
)
WHERE Row_Num BETWEEN 5 and 10
/
You may replace between operator with <= and >= if necessary.
Here's what I see in output:
DEPTNO ENAME SAL ROW_NUM
20 FORD 3000 5
30 JAMES 950 6
20 JONES 2975 7
10 KING 5000 8
30 MARTIN 1250 9
10 MILLER 1300 10
Using rownum is not a great idea, because there's no guarantee that the same rows will be assigned the same rownum values in different queries.
If the table has any combination of columns that uniquely identify a row, it is better to generate a ranking based on that and use that ranking to identify batches of rows. For example:
SELECT * FROM (
SELECT table.*, RANK() OVER (ORDER BY column1, column2) as my_rank
FROM table
)
WHERE my_rank >= 10000 AND my_rank < 20000
This will work with any range, and will be reproducible as long as the values in the columns used do not change and uniquely identify a row. (Actually, I think this would be usable even if they do not uniquely identify a row, as long as they work to break the rows into small enough batches.)
The downside is that MY_RANK will be included in the output. You can avoid that by explicitly listing the columns you do want to select; or it may be easier to filter it out when you are loading the data into the other database.
If you want to preserve the rowids, use the following SQL. This SQL took 4 minutes, 20 seconds to run against a 218 million row table on a 2 CPU server with 18 GB devoted to the DB.
CREATE TABLE rowids
AS
WITH
aset
AS
(SELECT ROWID AS row_id, row_number () OVER (ORDER BY ROWID) r
FROM amiadm.big_table)
SELECT *
FROM aset
WHERE MOD (r, 10000) = 0;
After creating this table, loop through it with the following:
BEGIN
FOR recs
IN ( SELECT row_id
, LAG (row_id) OVER (ORDER BY row_id) prev_row_id
, LEAD (row_id) OVER (ORDER BY row_id) next_row_id
FROM rowids
ORDER BY row_id)
LOOP
IF prev_row_id IS NULL
THEN
SELECT *
FROM big_table
WHERE ROWID <= recs.row_id;
ELSIF next_row_id IS NULL
THEN
SELECT *
FROM big_table
WHERE ROWID > row_id;
ELSE
SELECT *
FROM big_table
WHERE ROWID > prev_row_id
AND ROWID <= row_id;
END IF;
END LOOP;
END;

Oracle count with group by returning empty rows

I am trying to build a query where it returns the count for each month (passing a start and an end date) of a certain value, the output should be like this
Month Qtn
---------
|July|0|
|Augu|0|
|Sept|0|
but I get no rows, the query is the following:
SELECT TO_CHAR(TT_CUSTOMERS.CLOSED_DATE, 'YYYYMM') AS XMONTH, COUNT(TT_CUSTOMERS.NAMES) AS QTY FROM TT_CUSTOMERS
WHERE COMPANY = 700
AND TT_CUSTOMERS.C1_LOOKUP_ID = 100
AND TT_CUSTOMERS.C2_LOOKUP_ID = 1
AND TT_CUSTOMERS.C3_LOOKUP_ID IN (70, 80)
AND TT_CUSTOMERS.ST_LOOKUP_ID = 90
AND TO_NUMBER(TO_CHAR(TT_CUSTOMERS.CLOSED_DATE, 'YYYYMMDD')) >= TO_NUMBER('20170701')
AND TO_NUMBER(TO_CHAR(TT_CUSTOMERS.CLOSED_DATE, 'YYYYMMDD')) <= TO_NUMBER('20170930')
GROUP BY TO_CHAR(TT_CUSTOMERS.CLOSED_DATE, 'YYYYMM')
ORDER BY TO_CHAR(TT_CUSTOMERS.CLOSED_DATE, 'YYYYMM') ASC;
I know it's because the query is returning no rows, so the XMONTH is emtpy and so also the qty is empty, but I can't figure out a way to get the output that I've shown.
Assuming closed_date is of a date datatype...
and assuming all your other fields values are int and matches exist in the database...
I'd assume it's the numeric comparison of date values. Compare a date as a date not as numbers.
SELECT TO_CHAR(TT_CUSTOMERS.CLOSED_DATE, 'YYYYMM') AS XMONTH
, COUNT(TT_CUSTOMERS.NAMES) AS QTY
FROM TT_CUSTOMERS
WHERE COMPANY = 700
AND TT_CUSTOMERS.C1_LOOKUP_ID = 100
AND TT_CUSTOMERS.C2_LOOKUP_ID = 1
AND TT_CUSTOMERS.C3_LOOKUP_ID IN (70, 80)
AND TT_CUSTOMERS.ST_LOOKUP_ID = 90
AND TT_CUSTOMERS.CLOSED_DATE >= TO_DATE('20170701','YYYYMMDD')
AND TT_CUSTOMERS.CLOSED_DATE <= TO_DATE('20170930','YYYYMMDD')
GROUP BY TO_CHAR(TT_CUSTOMERS.CLOSED_DATE, 'YYYYMM')
ORDER BY TO_CHAR(TT_CUSTOMERS.CLOSED_DATE, 'YYYYMM') ASC;

select next record date minus 1 based on the logic

I want to achieve the following output through sql query
Below is the data in the oracle table
date 1 date 2 amt1 amt2
1/1/2012 12/31/2012 100 100
1/1/2013 11/31/2013 100 100
1/1/2014 9/31/2014 50 100
1/1/2015 12/31/2015 20 100
Desired output I need:
date 1 date 2 amt1 amt2
1/1/2012 12/31/2013 100 100
1/1/2014 12/31/2014 50 100
1/1/2015 12/31/9999 20 100
The logic to get the output would be if the amounts are same for multiple records then a single record should exist in the output with the earliest date as date1 . date 2 of the record written to the output should be next record effective date minus 1 , and if there is no next record found ,the date 2 should be defaulted to 31-dec-9999
(Note:whenever there is a change in amt1 or amt 2 between 2 records, those should be treated as separate records and if amts are same for all records then a single record should exist in the output )
Please let me know how to achieve this output through sql query
This solution uses a sub-query to aggregate a single row for each permutation of AMT1 and AMt2. Then it applies an analytic LEAD() function to get the next DATE1 and manipulates it according to the specification.
with yt as (
select amt1
, amt2
, min(date1) as date1
, max(date2) as date2
from your_table
group by amt1
, amt2
)
select yt.date1
, nvl(lead(yt.date1) over (order by yt.date1)-1, date '9999-12-31') as date2
, amt1
, amt2
from yt
order by yt.date1
/
By the way I had to correct your data (bad dates) when I used it in the inevitable SQL Fiddle; find it here.

How to find the difference between two timestamp column

I have table name Record which has the following columns, Empid in number column, dat in timestamp
Which has the following values
empid dat
====== ====
101 4/9/2012 9:48:54 AM
101 4/9/2012 9:36:28 AM
101 4/9/2012 6:16:28 PM
101 4/10/2012 9:33:48 AM
101 4/10/2012 12:36:28 PM
101 4/10/2012 8:36:12 PM
101 4/11/2012 9:36:28 AM
101 4/11/2012 4:36:22 PM
Here I need to display the following columns,
empid,min(dat) as start,max(dat) as end and difference(max(dat)-min(dat) for each day
Here 3 different days are exists so It should return 3 records with the above mentioned columns.
Please give some ways to get this.
Simply subtract them: max(dat) - min(dat)
SELECT empid,
min(dat) as strt,
max(dat) as end,
max(dat) - min(dat) as diff
FROM the_table
GROUP BY empid;
If you want to group by the day instead of the empid, use this one:
select trunc(dat),
min(dat) as strt,
max(dat) as end,
max(dat) - min(dat) as diff
from the_table
group by trunc(dat)
Date arithmetic is pretty straightforward in Oracle: the difference betwwen two dates is returned as the number of days. Values of less than a day are returned as *decimals". That is, 75 minutes is 1.25 hours not 1.15. If you want it as hours and minutes you need to work with an interval.
The inner query calculates the difference between the minimum and maximum data for each employee for each day, and converts it to a DAY interval. The outer query extracts the HOUR and MINUTES from that interval.
select empid
, tday
, sdt
, edt
, extract(hour from diff) diff_hours
, extract (minute from diff) diff_minutes
from (
select empid
, trunc(dat) tday
, min(dat) sdt
, max(dat) edt
, numtodsinterval(max(dat) - min(dat), 'DAY') diff
from t42
group by empid, trunc(dat)
)

Resources