简体   繁体   中英

Comparison of one row to another in SQLite

I have an Android app. Inside my Android app I have an SQLite database. Inside my SQLite database I have a table that looks like this:

_id    a    b    c    d
 1     1    1    1    0
 2     0    1    1    1
 3     1    0    0    1
 4     0    1    0    1

I want to calculate for each row: of all the columns a, b, c and d that have a 1 in EITHER the current or previous row, what percentage have a 1 in BOTH the current and previous row? The output would look like this:

_id    a    b    c    d    result
 1     1    1    1    0    NULL
 2     0    1    1    1    50%
 3     1    0    0    1    25%
 4     0    1    0    1    33%

I can do this in Java outside of SQLite, but I'd rather do it in SQL; it would be neater that way. What query should I use?

Select  CurAndNext.T1_id
    , Sum( Case When D1.Val + D2.Val = 1 Then 1 End ) As CntInEitherRow
    , Sum( Case When D1.Val + D2.Val = 2 Then 1 End ) / 4.000 As PercBoth
From    (
        Select T1._id As T1_id, Max( T2._id ) As T2_id
        From MyTable As T1
            Left Join MyTable As T2
                On T2._id < T1._id
        Group By T1._id
        ) As CurAndNext
    Join    (
            Select _id, 'a' As Col, a As Val From MyTable As T1
            Union All
            Select _id, 'b', b From MyTable As T1
            Union All
            Select _id, 'c', c From MyTable As T1
            Union All
            Select _id, 'd', d From MyTable As T1
            ) As D1
        On D1._id = CurAndNext.T1_id
    Left Join   (
                Select _id, 'a' As Col, a As Val From MyTable As T1
                Union All
                Select _id, 'b', b From MyTable As T1
                Union All
                Select _id, 'c', c From MyTable As T1
                Union All
                Select _id, 'd', d From MyTable As T1
                ) As D2
            On D2._id = CurAndNext.T2_id
                And D2.Col = D1.Col
Group By CurAndNext.T1_Id

A significant factor in making this query difficult is that the data is denormalized. Thus, I'm having to normalize it in order to get the information you seek.


Knowing what columns a , b , c and d represent makes all the difference in the world. The complexity of the above query is indicative of a schema that does not map well to the business needs. Knowing that they represent student attendance, we can devise an alternate schema.

Create Table Student
    (
    Id int not null Primary Key
    , Name varchar(50) not null
    )

Create Table Class
    (
    Id int not null Primary Key
    , Name varchar(50) not null
    )

-- if using dates, this would be the equivalent
-- of a calendar table
Create Table ClassDay 
    ( 
    DayNum int not null Primary Key 
    )

-- ClassDayNum would be better as a Date    
Create Table Attendence
    (
    StudentId int References Student( Id )
    , ClassId int References Class( Id )
    , ClassDayNum int not null  References ClassDay( DayNum )
    , Unique( StudentId, ClassId, ClassDayNum )
    )

Insert Student( Id, Name )
Select 1, 'a'
Union All Select 2, 'b'
Union All Select 3, 'c'
Union All Select 4, 'd'

Insert Class( Id, Name )
Values (1, 'Some Class' )

Insert ClassDay( DayNum )
Select 1
Union All Select 2
Union All Select 3
Union All Select 4

Insert Attendence( ClassId, StudentId, ClassDay )
Select 1, 1, 1
Union All Select 1, 1, 3
Union All Select 1, 2, 1
Union All Select 1, 2, 2
Union All Select 1, 2, 4
Union All Select 1, 3, 1
Union All Select 1, 3, 2
Union All Select 1, 4, 2
Union All Select 1, 4, 3
Union All Select 1, 4, 4

of all the columns a, b, c and d that have a 1 in EITHER the current or previous row

The way your results read you actually was requesting the number of people that attended on one day and not the previous or on the previous day and not the current .

Select Class.Id, ClassDay.DayNum
    , Count(Distinct A.StudentId) As Attendence
    , Count(Distinct A.StudentId) / 4.000 As Ratio
From Class
    Cross Join Student
    Cross Join ClassDay
    Left Join Attendence As A
        On A.ClassId = Class.Id
            And A.StudentId = Student.Id
            And A.ClassDayNum = ClassDay.DayNum
            And A.ClassDayNum > 1
    Left Join Attendence As A2
        On A2.ClassId = Class.Id
            And A2.StudentId = Student.Id
            And A2.ClassDayNum = ClassDay.DayNum - 1
Where Not( A.StudentId Is Not Null And A2.StudentId Is Not Null )
Group By Class.Id, ClassDay.DayNum

Results:

DayNum  Attendence | Ratio
1      |    0      |   0
2      |    1      |   .25
3      |    1      |   .25
4      |    1      |   .25

what percentage have a 1 in BOTH the current and previous row

Select ClassDay.DayNum
    , Sum( Case When A.StudentId Is Not Null And A2.StudentId Is Not Null Then 1 End )
    , Sum( Case When A.StudentId Is Not Null And A2.StudentId Is Not Null Then 1 End ) / 4.000
From Class
    Cross Join Student
    Cross Join ClassDay
    Left Join Attendence As A
        On A.ClassId = Class.Id
            And A.StudentId = Student.Id
            And A.ClassDayNum = ClassDay.DayNum
            And A.ClassDayNum > 1
    Left Join Attendence As A2
        On A2.ClassId = Class.Id
            And A2.StudentId = Student.Id
            And A2.ClassDayNum = ClassDay.DayNum - 1
Group By ClassDay.DayNum            
DayNum | Attendence | Ratio
1      |    NULL    |  NULL
2      |    2       |  0.500000
3      |    1       |  0.250000
4      |    1       |  0.250000

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM