Skip to Main Content

SQL & PL/SQL

Announcement

For appeals, questions and feedback about Oracle Forums, please email oracle-forums-moderators_us@oracle.com. Technical questions should be asked in the appropriate category. Thank you!

Identifying and grouping consecutive rows in sql

faanwarMay 10 2012 — edited May 12 2012
I have following data set:
CREATE TABLE APPS.T1
(
  ROW_NUM               NUMBER,
  EFFECTIVE_START_DATE  DATE                    NOT NULL,
  EFFECTIVE_END_DATE    DATE                    NOT NULL,
  STATUS                VARCHAR2(30 BYTE)
);


SET DEFINE OFF;
Insert into APPS.T1
   (ROW_NUM, EFFECTIVE_START_DATE, EFFECTIVE_END_DATE, STATUS)
 Values
   (1, TO_DATE('07/01/2009 00:00:00', 'MM/DD/YYYY HH24:MI:SS'), TO_DATE('09/06/2009 00:00:00', 'MM/DD/YYYY HH24:MI:SS'), 'VAC');
Insert into APPS.T1
   (ROW_NUM, EFFECTIVE_START_DATE, EFFECTIVE_END_DATE, STATUS)
 Values
   (2, TO_DATE('03/20/2011 00:00:00', 'MM/DD/YYYY HH24:MI:SS'), TO_DATE('03/31/2011 00:00:00', 'MM/DD/YYYY HH24:MI:SS'), 'VAC');
Insert into APPS.T1
   (ROW_NUM, EFFECTIVE_START_DATE, EFFECTIVE_END_DATE, STATUS)
 Values
   (3, TO_DATE('08/06/2011 00:00:00', 'MM/DD/YYYY HH24:MI:SS'), TO_DATE('08/22/2011 00:00:00', 'MM/DD/YYYY HH24:MI:SS'), 'VAC');
Insert into APPS.T1
   (ROW_NUM, EFFECTIVE_START_DATE, EFFECTIVE_END_DATE, STATUS)
 Values
   (4, TO_DATE('08/23/2011 00:00:00', 'MM/DD/YYYY HH24:MI:SS'), TO_DATE('08/26/2011 00:00:00', 'MM/DD/YYYY HH24:MI:SS'), 'VAC');
Insert into APPS.T1
   (ROW_NUM, EFFECTIVE_START_DATE, EFFECTIVE_END_DATE, STATUS)
 Values
   (5, TO_DATE('08/27/2011 00:00:00', 'MM/DD/YYYY HH24:MI:SS'), TO_DATE('08/27/2011 00:00:00', 'MM/DD/YYYY HH24:MI:SS'), 'VAC');
COMMIT;


SELECT * FROM APPS.T1

   ROW_NUM EFFECTIVE EFFECTIVE STATUS                        
---------- --------- --------- ------------------------------
         1 01-JUL-09 06-SEP-09 VAC                           
         2 20-MAR-11 31-MAR-11 VAC                           
         3 06-AUG-11 22-AUG-11 VAC                           
         4 23-AUG-11 26-AUG-11 VAC                           
         5 27-AUG-11 27-AUG-11 VAC                           

5 rows selected.
My requirement was that row number 3, 4 and 5 be grouped and treated as a single vacation record such that

effective_start_date = 06-AUG-2011 and
effective_end_date = 27-AUG-2011


For this I wrote a query:
SELECT effective_start_date,
       effective_end_date,
       CASE
          WHEN LAG (effective_end_date, 1) OVER (ORDER BY row_num) + 1 = effective_start_date
             THEN 0
          WHEN LEAD (effective_start_date, 1) OVER (ORDER BY row_num) - 1 = effective_end_date
             THEN 0
          ELSE 1
       END row_num
  FROM (SELECT * FROM T1)
Now the data returned looks like:
EFFECTIVE EFFECTIVE    ROW_NUM
--------- --------- ----------
01-JUL-09 06-SEP-09          1
20-MAR-11 31-MAR-11          1
06-AUG-11 22-AUG-11          0
23-AUG-11 26-AUG-11          0
27-AUG-11 27-AUG-11          0

5 rows selected.
Now I can easily use MIN(effective_start_date) and MAX(effective_start_date) group by ROW_NUM to achieve the desired results
SELECT   MIN (effective_start_date) start_dt,
         MAX (effective_start_date) end_dt,
         row_num
    FROM (SELECT effective_start_date,
                 effective_end_date,
                 CASE
                    WHEN LAG (effective_end_date, 1) OVER (ORDER BY row_num) + 1 = effective_start_date
                       THEN 0
                    WHEN LEAD (effective_start_date, 1) OVER (ORDER BY row_num) - 1 = effective_end_date
                       THEN 0
                    ELSE 1
                 END row_num
            FROM (SELECT *
                    FROM t1))
GROUP BY row_num
  HAVING row_num = 0
UNION
SELECT effective_start_date start_dt,
       effective_start_date end_dt,
       row_num
  FROM (SELECT effective_start_date,
               effective_end_date,
               CASE
                  WHEN LAG (effective_end_date, 1) OVER (ORDER BY row_num) + 1 = effective_start_date
                     THEN 0
                  WHEN LEAD (effective_start_date, 1) OVER (ORDER BY row_num) - 1 = effective_end_date
                     THEN 0
                  ELSE 1
               END row_num
          FROM (SELECT *
                  FROM t1))
 WHERE row_num = 1

START_DT  END_DT       ROW_NUM
--------- --------- ----------
01-JUL-09 01-JUL-09          1
20-MAR-11 20-MAR-11          1
06-AUG-11 27-AUG-11          0

3 rows selected.
All done BUT the problem is that there may be several groups of consecutive rows like this. In that case each group should be identified distinctly for GROUP BY clause to work as expected.

I want to assign a unique number to each occurence of such group.

How can I achieve this? Any ideas?

Regards,

Faraz

Edited by: faanwar on May 10, 2012 3:36 PM

Comments

Alex Keh-Oracle

Did you add the required Fluent API (i.e. HasMaxLength(x > 2000)) so that ODP.NET EF Core can map a byte[] to a BLOB?

user606112

My fluent API looks like this:

        protected override void OnModelCreating(ModelBuilder modelBuilder)

        {

            //Default - Schema: HR

            modelBuilder.HasDefaultSchema("HR");

            // Customer-Configuration

            modelBuilder.Entity<Customer>().Property(c => c.CompanyName).HasColumnType("varchar2");

            modelBuilder.Entity<Customer>().Property(c => c.CompanyName).HasMaxLength(40);

            modelBuilder.Entity<Customer>().Property(c => c.Ludsys).IsConcurrencyToken();

            // Order-Configuration

            modelBuilder.Entity<Order>().Property(o => o.Ludsys).IsConcurrencyToken();

            // OrderDetail-Configuration

            modelBuilder.Entity<OrderDetail>().Property(od => od.Ludsys).IsConcurrencyToken();

            // Product-Configuration

            modelBuilder.Entity<Product>().Property(p => p.ProductName).HasColumnType("varchar2");

            modelBuilder.Entity<Product>().Property(p => p.ProductName).HasMaxLength(40);

            modelBuilder.Entity<Product>().Property(p => p.Ludsys).IsConcurrencyToken();

            // Picture-Configuration

            modelBuilder.Entity<Picture>().Property(p => p.Bild).HasColumnType("blob");

            modelBuilder.Entity<Product>().Property(p => p.ProductName).HasMaxLength(6000000); // 6MB

            modelBuilder.Entity<Picture>().Property(p => p.Ludsys).IsConcurrencyToken();

            // Beziehungen der DbSets untereinander

            modelBuilder.Entity<Customer>().HasMany(c => c.Orders).WithOne(o => o.Customer);

            modelBuilder.Entity<Order>().HasMany(o => o.OrderDetails).WithOne(od => od.Order);

            modelBuilder.Entity<OrderDetail>().HasMany(od => od.Pictures).WithOne(p => p.OrderDetail);

            modelBuilder.Entity<Product>().HasMany(p => p.OrderDetails).WithOne(od => od.Product);

        } // OnModelCreating

Alex Keh-Oracle

Have you tried adding the required HasMaxLength(x > 2000) Fluent API to the following line:

       modelBuilder.Entity<Picture>().Property(p => p.Bild).HasColumnType("blob");

This Fluent API is required as per the documentation.

user606112

I tried 3 variants:

only: modelBuilder.Entity<Picture>().Property(p => p.Bild).HasColumnType("blob");

           

only: modelBuilder.Entity<Product>().Property(p => p.ProductName).HasMaxLength(6000000); // 6MB

both: modelBuilder.Entity<Picture>().Property(p => p.Bild).HasColumnType("blob");

         modelBuilder.Entity<Product>().Property(p => p.ProductName).HasMaxLength(6000000); // 6MB

I get always: ORA-01460

Alex Keh-Oracle
Answer

Have you tried the HasMaxLength on the Bild property, not the ProductName?

modelBuilder.Entity<Picture>().Property(p => p.Bild).HasMaxLength(6000000);

It should work with the HasMaxLength, but without the HasColumnType.

Marked as Answer by user606112 · Sep 27 2020
user606112

Hallo Alex,

thank you very much for your help. Only the following property is necessary.

modelBuilder.Entity<Picture>().Property(p => p.Bild).HasMaxLength(6000000);

I did much copy and paste to resolve the problem, so that I didn't see the wrong columnname "productname".

Now all is working pretty .

With kind regards! Günter Hoormann

Alex Keh-Oracle

Hi Gunter,

Glad to hear it works for you. Which DB version are you using?

My team hadn't seen this error before when we tested similar scenarios.

user606112

Hallo Alex,

I use DB-Version as below to create my Test-DB:

BANNER

-------------------------------------------------------------------

Oracle Database 18c Express Edition Release 18.0.0.0.0 - Production

BANNER_FULL

-------------------------------------------------------------------

Oracle Database 18c Express Edition Release 18.0.0.0.0 - Production

Version 18.4.0.0.0

BANNER_LEGACY

-------------------------------------------------------------------

Oracle Database 18c Express Edition Release 18.0.0.0.0 - Production

I created the DB by application in VisualStudio 2019 using the following nuget-packages:

Util1.JPG

DB looks like this:

Util3.JPG

Util4.JPG

I tested EF Core 3.1 using packages:

Util2.JPG

Actual Fluent-API:

    protected override void OnModelCreating(ModelBuilder modelBuilder)

    {

        //Default - Schema: HR

        modelBuilder.HasDefaultSchema("HR");

        // Customer-Configuration

        modelBuilder.Entity\<Customer>().Property(c => c.CompanyName).HasMaxLength(40);

        modelBuilder.Entity\<Customer>().Property(c => c.Ludsys).IsConcurrencyToken();

        // Order-Configuration

        modelBuilder.Entity\<Order>().Property(o => o.Ludsys).IsConcurrencyToken();

        // OrderDetail-Configuration

        modelBuilder.Entity\<OrderDetail>().Property(od => od.Ludsys).IsConcurrencyToken();

        // Product-Configuration

        modelBuilder.Entity\<Product>().Property(p => p.ProductName).HasMaxLength(40);

        modelBuilder.Entity\<Product>().Property(p => p.Ludsys).IsConcurrencyToken();

        //Picture - Configuration

        modelBuilder.Entity\<Picture>().Property(p => p.Bild).HasMaxLength(6000000); // 6MB

        modelBuilder.Entity\<Picture>().Property(p => p.Ludsys).IsConcurrencyToken();

        // Beziehungen der DbSets untereinander

        modelBuilder.Entity\<Customer>().HasMany(c => c.Orders).WithOne(o => o.Customer);

        modelBuilder.Entity\<Order>().HasMany(o => o.OrderDetails).WithOne(od => od.Order);

        modelBuilder.Entity\<OrderDetail>().HasMany(od => od.Pictures).WithOne(p => p.OrderDetail);

        modelBuilder.Entity\<Product>().HasMany(p => p.OrderDetails).WithOne(od => od.Product);

    } // OnModelCreating

For ludsys column I created some trigger like this:

-- Customers

create or replace trigger trg_Customers_Ludsys

before insert or update on "Customers"

for each row

begin

:new."Ludsys" := sysdate;

end trg_Customers_Ludsys;

/

I hope this will help you.

Günter

1 - 8
Locked Post
New comments cannot be posted to this locked post.

Post Details

Locked on Jun 9 2012
Added on May 10 2012
8 comments
20,964 views