In one of my apps I need to calculate fuel consumption that takes into account complete refuelings and partly refuelings. My Android App is based on SQLite syntax and features.
I try to solve that puzzle in SQL and don't want to walk thru all rows to do this stuff programmatically.
Rows with FULL='N' should be left out in the result list but their values should be added to the next FULL='Y' row. At least that is my idea.
This is the DML (only required columns shown):
CREATE TABLE FUELINGS (
ID INTEGER PRIMARY KEY,
MILEAGE INTEGER,
VOLUME DECIMAL(8, 2),
FULL CHAR(1));
Here's some input to this table:
INSERT INTO FUELINGS VALUES(1,22995,29.48,'Y');
INSERT INTO FUELINGS VALUES(2,23385,31.27,'Y');
INSERT INTO FUELINGS VALUES(3,23869,16.71,'N');
INSERT INTO FUELINGS VALUES(4,24065,33.18,'Y');
INSERT INTO FUELINGS VALUES(5,24485,30.59,'Y');
INSERT INTO FUELINGS VALUES(6,24956,33.78,'Y');
Ok, here comes the problem. This is the query I use:
SELECT T2.MILEAGE,
T2.VOLUME,
T2.FULL,
T2.PREVMILEAGE,
T2.PREVVOLUME,
T2.PREVFULL,
100 * T2.VOLUME / ( T2.MILEAGE - T2.PREVMILEAGE )
FROM (SELECT T.MILEAGE,
T.VOLUME,
T.FULL,
T.PREVMILEAGE,
(SELECT VOLUME
FROM FUELINGS
WHERE MILEAGE = T.PREVMILEAGE
AND MILEAGE IS NOT NULL
AND MILEAGE > 0
AND VOLUME IS NOT NULL
AND VOLUME > 0) AS PREVVOLUME,
(SELECT FULL
FROM FUELINGS
WHERE MILEAGE = T.PREVMILEAGE
AND MILEAGE IS NOT NULL
AND MILEAGE > 0
AND VOLUME IS NOT NULL
AND VOLUME > 0) AS PREVFULL
FROM (SELECT F.MILEAGE,
F.VOLUME,
F.FULL,
(SELECT Max(MILEAGE)
FROM FUELINGS PREV
WHERE PREV.MILEAGE < F.MILEAGE
AND MILEAGE IS NOT NULL
AND MILEAGE > 0
AND VOLUME IS NOT NULL
AND VOLUME > 0) AS PREVMILEAGE
FROM FUELINGS F
WHERE MILEAGE IS NOT NULL
AND MILEAGE > 0
AND VOLUME IS NOT NULL
AND VOLUME > 0) AS T
WHERE PREVMILEAGE IS NOT NULL) AS T2
ORDER BY T2.MILEAGE;
This is the result:
T2.MILEAGE T2.VOLUME T2.FULL T2.PREVMILEAGE T2.PREVVOLUME T2.PREVFULL 100 * T2.VOLUME / (T2.MILEAGE - T2.PREVMILEAGE)
23385 31.27 Y 22995 29.48 Y 8.017948717948718
23869 16.71 N 23385 31.27 Y 3.4524793388429753
24065 33.18 Y 23869 16.71 N 16.928571428571427
24485 30.59 Y 24065 33.18 Y 7.283333333333333
24956 33.78 Y 24485 30.59 Y 7.171974522292993
16.92 is wrong because the refueling before that one was a partly refueling. I need to sum and calculate both rows. I am aware that there might be more than 1 partly refueling. But I don't have any clue how to build ranges of FULL='N' values, sum them up and add them to the following FULL='Y' value.
Can please somebody put me in the right direction. How can I make that right?
Many thanks in advance.
EDIT/SOLUTION:
SELECT T.MILEAGE,
T.PREVMILEAGE,
SUM(VOLUME),
100 * SUM(VOLUME) / ( T.MILEAGE - T.PREVMILEAGE )
FROM (SELECT F1.MILEAGE AS MILEAGE,
MAX(F2.MILEAGE) AS PREVMILEAGE
FROM FUELINGS F1
LEFT OUTER JOIN FUELINGS F2
ON F1.MILEAGE > F2.MILEAGE
AND F2.FULL = 'Y'
AND F2.MILEAGE IS NOT NULL
AND F2.MILEAGE > 0
AND F2.VOLUME IS NOT NULL
AND F2.VOLUME > 0.0
WHERE F1.FULL = 'Y'
AND F1.MILEAGE IS NOT NULL
AND F1.MILEAGE > 0
AND F1.VOLUME IS NOT NULL
AND F1.VOLUME > 0.0
GROUP BY F1.MILEAGE) AS T,
(SELECT VOLUME
FROM FUELINGS F3
WHERE F3.MILEAGE > T.PREVMILEAGE
AND F3.MILEAGE <= T.MILEAGE)
GROUP BY T.MILEAGE
Your original query would work if you had a table called FuelingsFull, which only contained complete information about full fuelings. This can be summarized from Fueling. The process would be simpler with window functions, but you don't have those in SQLite.
So, here is an idea on how to do this. First, get the previous full id:
select f1.id, max(f2.full) as prevFullId
from fuelings f1 left outer join
fuelings f2
on f1.id > f2.id and
f2.full = 0
group by f1.id
Now, you have a group of fuelings that all belong together. The next query joins this back to the fuelings and then groups these together.
select fills.id, max(mileage) as mileage, sum(volume) as volume, count(*) as numFills
from fuelings f join
(select f1.id, max(f2.full) as prevFullId
from fuelings f1 left outer join
fuelings f2
on f1.id > f2.id and
f2.full = 1
group by f1.id
) fills
on f.id > fills.prevFullId and f.id <= fills.id
group by fills.id
With this as your FuelingFull, you original query should work.
Related
So I have a table with 4 columns and would like to sum the values of the amount column where isExpense is true and where isExpense is false. I would like to subtract those 2 values and return that sum.
I don't have much experience with SQL other than single line queries so I'm struggling to format this.
#Query("""
SELECT SUM (amount) AS INCOME FROM `transaction` WHERE isExpense = 0,
SELECT SUM (amount) AS EXPENSE FROM `transaction` WHERE isExpense = 1,
SUM (INCOME - EXPENSE) AS BALANCE
""")
fun getTotalBalance(): Flow<Double>?
I could get around this by creating more columns in my table if all else fails.
Use case expressions to do conditional aggregation:
SELECT SUM(case when isExpense = 0 then amount else 0 end) AS INCOME,
SUM(case when isExpense = 1 then amount else 0 end) AS EXPENSE,
SUM(case when isExpense = 0 then amount
when isExpense = 1 then -amount
end) as BALANCE
FROM `transaction`
WHERE isExpense IN (0, 1) -- Not needed, but might speed things up if there
-- are other values than 0 and 1
The following SQLite query works fine on android 4.4 and below, but it causes an exception: "android.database.sqlite.SQLiteException: ambiguous column name: number (code 1):......" on android 5.0 and later. I checked the SQLite release documents, but did not see any changes that can effect my work. Is there any thing that I missing?
select * from
(
select
'0' as queryid,
CNCT._id,
coalesce(case when length(C.abId)=0 then null else C.abId end,
(
select
addrBkId from numbers as nm,
contacts as cot
where nm.number=coalesce(C.number,S.Number) and nm.contactID = cot._id
order by cot.lastMod desc limit 1)) as addrBkId,
coalesce(
case when length(C.abId)=0 then null else C.abId end,
(
select
addrBkId from numbers as nm,
contacts as cot
where nm.number=coalesce(C.number,S.Number) and nm.contactID = cot._id
order by cot.lastMod desc
limit 1
)
) as uqAddrBkId,
CNCT.displayName,
CNCT.firstName,
CNCT.lastName,
CNCT.favorite,
coalesce(C.location,
(
select
location from calls as css
where css.number = S.Number
)) as location,
0 as numberType,
coalesce(C.number,S.Number) number,
N.txt,A.type,
coalesce(A.callID,A.smsId) actId,
max(A.startEpoch) as maa,
max(A.startTime),
strftime('%w',datetime(A.startEpoch,'unixepoch','localtime'))+0 dayOfWeek,
strftime('%W',datetime(A.startEpoch,'unixepoch','localtime'))+0 weekOfYear,C.duration,
case
when C.callResult='vmail' then 'vmail'||C._id
when C.callType='callin' and C.callResult='missed' then 'missed'
else C.callType end as newCallType,
C.callResult,
C.extension,
C.msgId,
C.audioUrl,
C.name,
C.state,
C.syncParams,
S.smsId,
S.dir,
S.state,
N.noteId,
N.color from activity as A
left outer join calls C on A.callId=C.callId
left outer join sms S on A.smsId=S.smsId
left outer join contacts CNCT on coalesce(case when length(C.abId)=0 then null else C.abId end,
(
select addrBkId from numbers as nm,
contacts as cot
where nm.number=coalesce(C.number,S.Number) and nm.contactID = cot._id
order by cot.updated desc
limit 1)
)=CNCT.addrBkId
left outer join
(
select * from notes as nt
order by nt.lastMod asc
) as N on CNCT.addrBkId=N.addrBkId
where (C.state<>5 or C.state is NULL) and (C.callResult<>'abandoned' or C.callResult is NULL)
group by newCallType,number,weekOfYear,dayOfWeek
order by max(A.startEpoch) asc
)
group by _id
order by maa desc
limit 3
... where nm.number=coalesce(C.number,S.Number) ...
... where nm.number=coalesce(C.number,S.Number) ...
... where css.number = S.Number) ...
... coalesce(C.number,S.Number) ...
... where nm.number=coalesce(C.number,S.Number) ...
... group by newCallType,number,...
^^^^^^
All occurences of number are qualified with a table alias, except the last one. That one indeed is ambiguous.
I have 3 Tables
1 Customers
-c_id
-c_name
2 Debit_Master
-transaction_id
-c_id
-amount
3 Credit_Master
-transaction_id
-c_id
-amount
Now i want data like this : Customer Name and Total amount(credit amount - debit amount) of each customer .
I want query to fetch data in my listview with two columns 1- Customer Name , 2- Total amount
Try this SQL statement:
SELECT
Customers.c_name as CustomerName,
SUM((CASE
WHEN Credit_Master.amount IS NULL THEN 0
ELSE Credit_Master.amount END -
CASE
WHEN Debit_Master.amount IS NULL THEN 0
ELSE Debit_Master.amount END)) as TotalAmount
FROM Customers
LEFT JOIN Debit_Master on Customers.c_id = Debit_Master.c_id
LEFT JOIN Credit_Master on Customers.c_id = Credit_Master.c_id
GROUP BY Customers.c_id
Try something like this
SELECT Customers.c_name as name , (Credit_Master.amount - Debit_Master.amount) as total FROM Customers JOIN Debit_Master on Customers.c_id=Debit_Master.c_id JOIN Credit_Master on Customers.c_id=Credit_Master.c_id
i have select query as follows
SELECT DISTINCT tt.firstname,
tt.lastname,
tc.caseid,
tt.courtcode AS courtid,
tcou.courtname,
(SELECT COUNT(*)
FROM tblcasetrafficticketlink
WHERE caseid = tc.caseid) AS ticketcount,
Max(tt.violationdate) AS violationdate,
( tt.address1
|| ','
|| tt.address2 ) AS address,
tt.city,
tt.state,
tt.zip,
tt.dob,
tt.sex
FROM tblcase tc
LEFT OUTER JOIN tblcasetrafficticketlink tcttl
ON tc.caseid = tcttl.caseid
LEFT OUTER JOIN tbltraffictickets tt
ON tcttl.courtid = tt.courtcode
AND tt.ticketnumber = tcttl.ticketnumber
AND ( tcttl.ticketextension = tt.ticketnumberex
OR tt.ticketnumberex IS NULL )
LEFT OUTER JOIN tblcourts tcou
ON tcou.courtid = tt.courtcode
WHERE tc.casetype = 'TRAFFIC'
AND tc.caseid<='"+recent_min_caseID+"'
GROUP BY tc.caseid,
tt.firstname,
tt.lastname,
tt.dob,
tt.sex,
tt.courtcode,
tcou.courtname,
tt.city,
tt.state,
tt.zip,
tc.casestatus,
tt.address1,
tt.address2
ORDER BY tc.caseid DESC
LIMIT 100;
this is taking much time to get data. can anybody help to improve performance.Here PRAGMA is useful? if so how? if not, tell me the way to fix this issue.
I've found that SQLite on Android seems to have some... unexpected quirks. In my case, it turned out that doing a straight query like select * from Foo where Bar is null was MUCH slower than selecting only the IDs and then fetching each row by ID individually. YMMV.
Here is SQL UPDATE I'm trying to execute:
UPDATE T
SET T.CurrentStopNumber = TS.CurrentStopNumber
FROM Trip T
INNER JOIN (SELECT TripId, MIN(StopNumber) CurrentStopNumber
FROM TripStop
WHERE TripId = '106504'
AND (IsPickup = 1 OR IsDrop = 1)
AND StopNumber > (SELECT COALESCE(max(StopNumber), 0)
FROM TripUpdate
WHERE TripId = '106504'
AND Type = 2)) TS ON T.TripId = TS.TripId
I get error on second line:
/* Error message: SQL script is wrong mismatched input . expecting "EQ" */
I am familiar with SQL Server and I'm sure it would run on SQL Server just fine. Subquery runs fine and returns 1 row as I expect. I just need to update table with that value. What is wrong?
Use:
UPDATE TRIP
SET currentstopnumber = (SELECT MIN(ts.stopnumber)
FROM TRIPSTOP ts
WHERE ts.tripid = TRIP.tripid
AND ts.tripid = '106504'
AND 1 IN (ts.ispickup, ts.isdrop)
AND ts.stopnumber > (SELECT COALESCE(MAX(tu.stopnumber), 0)
FROM TRIPUPDATE tu
WHERE tu.tripid = ts.tripid
AND tu.type = 2)
GROUP BY ts.tripid)
WHERE EXISTS (SELECT NULL
FROM TRIPSTOP ts
WHERE ts.tripid = TRIP.tripid
AND ts.tripid = '106504'
AND 1 IN (ts.ispickup, ts.isdrop)
AND ts.stopnumber > (SELECT COALESCE(MAX(tu.stopnumber), 0)
FROM TRIPUPDATE tu
WHERE tu.tripid = ts.tripid
AND tu.type = 2))
The JOIN is preferable, but the syntax isn't widely supported.
I don't think that you should specify the table for the fields that you set, as the database already knows which table to update:
SET CurrentStopNumber = TS.CurrentStopNumber
(IIRC, that is not allowed in SQL Server either.)