How to get balance according date and ID for every customer_id and currency_id using INNER JOIN - sqlite

I have a table named as transaction_table:
CREATE TABLE transaction_table
(
_id INTEGER PRIMARY KEY AUTOINCREMENT,
date TEXT,
debit REAL,
credit REAL,
curr_id INTEGER,
cus_id INTEGER,
FOREIGN KEY (curr_id) REFERENCES currencies(_id) ON DELETE CASCADE,
FOREIGN KEY (cus_id) REFERENCES customers(_id) ON DELETE CASCADE
)
And assume this data in it:
_id date debit credit curr_id cus_id
-------------------------------------------------------------------
1 2022-12-08T00:00:00.000 10.0 0.0 1 1
2 2022-12-07T00:00:00.000 0.0 20.0 1 1
3 2022-12-06T00:00:00.000 0.0 30.0 1 1
4 2022-12-07T00:00:00.000 40.0 0.0 1 1
5 2022-12-08T00:00:00.000 100.0 0.0 1 1
So I just make SELECT statement that will get balance according date then _id but the balance is wrong:
SELECT t1._id,
t1.date ,
t1.description ,
t1.debit ,
t1.credit,
SUM(t2.debit - t2.credit) as blnc,
t1.curr_id,
t1.cus_id
FROM transaction_table t1 INNER JOIN transaction_table t2
ON t2.curr_id = t1.curr_id AND t2.cus_id = t1.cus_id AND t2._id <= t1._id AND t2.date <= t1.date
GROUP BY t1._id
ORDER BY t1.date DESC, t1._id DESC;
the result:
_id date debit credit balance curr_id cus_id
-----------------------------------------------------------------------------
5 2022-12-08T00:00:00.000 100.0 0.0 100.0 1 1
1 2022-12-08T00:00:00.000 10.0 0.0 10.0 1 1
4 2022-12-07T00:00:00.000 40.0 0.0 -10.0 1 1
2 2022-12-07T00:00:00.000 0.0 20.0 -20.0 1 1
3 2022-12-06T00:00:00.000 0.0 30.0 -30.0 1 1
the result is wrong and it should be like this:
_id date debit credit balance curr_id cus_id
-----------------------------------------------------------------------------
5 2022-12-08T00:00:00.000 100.0 0.0 100.0 1 1
1 2022-12-08T00:00:00.000 10.0 0.0 0.0 1 1
4 2022-12-07T00:00:00.000 40.0 0.0 -10.0 1 1
2 2022-12-07T00:00:00.000 0.0 20.0 -50.0 1 1
3 2022-12-06T00:00:00.000 0.0 30.0 -30.0 1 1
which I can achieve it by this code:
SELECT _id,date, description, debit, credit,
SUM(debit - credit) OVER (PARTITION BY curr_id, cus_id ORDER BY date, _id) blnc,
curr_id, cus_id
FROM transaction_table
order by date desc, _id desc;
but I don't need it cause its not working in old android.
I just need to use INNER JOIN.

The conditions about the dates and the ids in the ON clause need correction:
SELECT t1._id,
t1.date,
t1.description,
t1.debit,
t1.credit,
SUM(t2.debit - t2.credit) AS blnc,
t1.curr_id,
t1.cus_id
FROM transaction_table t1 INNER JOIN transaction_table t2
ON t2.curr_id = t1.curr_id AND t2.cus_id = t1.cus_id
AND (t2.date < t1.date OR (t2.date = t1.date AND t2._id <= t1._id))
GROUP BY t1._id
ORDER BY t1.date DESC, t1._id DESC;
See the demo.

You may substitute the SUM() analytic function call with a correlated subquery:
SELECT _id, date, description, debit, credit,
(SELECT SUM(t2.debit - t2.credit)
FROM transaction_table t2
WHERE t2.curr_id = t1.curr_id AND
t2.cus_id = t1.cus_id AND
(t2.date <= t1.date OR
t2.date = t1.date AND t2._id <= t1._id)) AS blnc,
curr_id, cus_id
FROM transaction_table t1
ORDER BY date DESC, _id DESC;

Related

how to get balance per recored in sqlite

I have table called transaction_table so I write the this code
select
t._id,
t.description,
t.debit,
t.credit,
(t.debit - t.credit) blnc,
t.curr_id,
t.cus_id
from transaction_table t
the result:
this col
_id description debit credit blnc curr_id cuss_id
-------------------------------------------------------------------------
1 cr for customer 1 0.0 30.0 -30.0 1 1
2 cr for customer 1 0.0 500.0 -500.0 1 1
3 dbt for customer 1 70.0 0.0 70.0 1 1
4 cr for customer 2 0.0 600.0 -600.0 1 2
5 dat for customer 2 100.0 0.0 100.0 1 2
6 dat for customer 2 300.0 0.0 300.0 1 2
but I want some thing like this:
_id description debit credit blnc curr_id cuss_id
-------------------------------------------------------------------------
1 cr for customer 1 0.0 30.0 -30.0 1 1
2 cr for customer 1 0.0 500.0 -530.0 1 1
3 dbt for customer1 70.0 0.0 470.0 1 1
4 cr for customer 2 0.0 600.0 -600.0 1 2
5 dat for customer 2 100.0 0.0 -500.0 1 2
6 dat for customer 2 300.0 0.0 -200.0 1 2
So, how to achieve the second table using sqlite? thank you.
Use SUM() window function:
SELECT _id, description, debit, credit,
SUM(debit - credit) OVER (PARTITION BY curr_id, cus_id ORDER BY _id) blnc,
curr_id, cus_id
FROM transaction_table;
Depending on your requirement you may remove the column curr_id from the PARTITION BY clause.
For versions of SQLite prior to 3.25, that do not support window functions, use a self join and aggregation:
SELECT t1._id, t1.description, t1.debit, t1.credit,
SUM(t2.debit - t2.credit) blnc,
t1.curr_id, t1.cus_id
FROM transaction_table t1 INNER JOIN transaction_table t2
ON t2.curr_id = t1.curr_id AND t2.cus_id = t1.cus_id AND t2._id <= t1._id
GROUP BY t1._id;
Depending on your requirement you may remove the condition t2.curr_id = t1.curr_id from the ON clause.
See the demo.

SQLite: Group data within certain time interval

I have a single table which stores data of orders:
Orders Table:
id | order_time | quantity | ...
1 | 1592821854318 | 2
2 | 1592901538199 | 4
3 | 1592966454547 | 1
4 | 1593081282406 | 9
5 | 1593141826330 | 6
order_time table is UNIX timestamp.
Using below query I am able to get available data grouped by days (86400000 = 24 hours):
SELECT order_time+ (86400000 - (order_time % 86400000)) as gap, SUM(quantity) as
totalOrdersBetweenInterval
FROM USAGE_DETAILS ud
WHERE order_time >= 1590969600 AND order_time <= 1593388799000
GROUP BY gap
ORDER BY gap ASC
Suppose for this month of June, I receive order on 1, 4, 6, 7 date then by using above query I am able to retrieve data as follow :
gap | totalOrdersBetweenInterval
1 | 5
4 | 6
6 | 4
7 | 10
I would receive UNIX timestamp in gap column but for the sake of example I have used readable dates.
Above query will only retrieve data for the days which would have received order but I want to split data in range like below which also include days with no orders :
gap | totalOrdersBetweenInterval
1 | 5
2 | 0
3 | 0
4 | 6
5 | 0
6 | 4
7 | 10
8 | 0
9 | 0
. | .
. | .
How do I go about that?
You need a query that returns 30 rows:1,2,...,30 for the days of June.
You could do it with a recursive CTE:
with days as (
select 1 day
union all
select day + 1
from days
where day < 30
)
but I'm not sure if Android uses a version of SQLite that supports CTEs.
If it does support them, all you need to do is join the CTE with a LEFT join to your query:
with
days as (
select 1 day
union all
select day + 1
from days
where day < 30
),
yourquery as (
<your query here>
)
select d.day, coalesce(t.totalOrdersBetweenInterval, 0) totalOrdersBetweenInterval
from days d left join yourquery t
on t.gap = d.day
If Android does not support CTEs you will have to build the query that returns the days with UNION ALL:
select d.day, coalesce(t.totalOrdersBetweenInterval, 0) totalOrdersBetweenInterval
from (
select 1 day union all select 2 union all
select 3 union all select 4 union all
......................................
select 29 union all select 30
) d left join (
<your query here>
) t
on t.gap = d.day
Thanks to #forpas for helping me out.
Just posting in case someone is searching for slicing data by unix time intervals.
with
days as (
select 1590969600000 day --Starting of June 1 2020
union all
select day + 86400000 --equivalent to 1 day
from days
where day < 1593388799000 --Less than 28th of June
),
subquery as (
SELECT order_time+ (86400000 - (order_time % 86400000)) as gap, SUM(quantity) as
totalOrdersBetweenInterval
FROM USAGE_DETAILS ud
WHERE order_time >= 1590969600000 AND order_time <= 1593388799000
GROUP BY gap
)
select d.day, coalesce(t.totalOrdersBetweenInterval, 0) totalOrdersBetweenInterval
from days d left join subquery t
on t.gap = d.day
order by d.day

Eliminating decimals from whole numbers only

I have a column with a range of numbers.
row1: 3.4
row2: 4.00
row3: 5.23
row4: 2.0
row5: 0.00
row6: 000
I would like the final result to be
row1: 3.4
row2: 4
row3: 5.23
row4: 2
row5: 0
row6: 0
I basically want to remove decimals if it's a whole number or 0.
A simple method, assuming they are all represent valid numbers, is just to convert them from strings to numbers and back again:
with t (id, str) as (
select 1, '3.4' from dual
union all select 2, '4.00' from dual
union all select 3, '5.23' from dual
union all select 4, '2.0' from dual
union all select 5, '0.00' from dual
union all select 6, '000' from dual
)
select id, str, to_char(to_number(str)) as result
from t;
ID STR RESULT
---------- ---- ----------------------------------------
1 3.4 3.4
2 4.00 4
3 5.23 5.23
4 2.0 2
5 0.00 0
6 000 0
That will show an initial value of 0.1 as just .1; if you want to preserve the leading zero in that case you could use:
rtrim(to_char(to_number(str), 'FM999999990.99999'), '.')
supplying a format mask with enough leading and trailing 9 placeholders for your data:
with t (id, str) as (
select 1, '3.4' from dual
union all select 2, '4.00' from dual
union all select 3, '5.23' from dual
union all select 4, '2.0' from dual
union all select 5, '0.00' from dual
union all select 6, '000' from dual
union all select 7, '01' from dual
union all select 8, '00.10' from dual
)
select id, str, rtrim(to_char(to_number(str), 'FM999999990.99999'), '.') as result
from t;
ID STR RESULT
---------- ----- ----------------
1 3.4 3.4
2 4.00 4
3 5.23 5.23
4 2.0 2
5 0.00 0
6 000 0
7 01 1
8 00.10 0.1
Another option is to just convert to a number in your query and let the client or whatever else consumes this decide how to format it.

SQL Count Data 1/2 hourly

I have a stored procedure that counts data for each hour,
Declare #DateTimeToFilter DATETIME;
--set #DateTimeToFilter = GetDate();
set #DateTimeToFilter = '6/5/14'
SET NOCOUNT ON;
WITH H ([Hour]) AS
( SELECT 7 UNION
SELECT 8 UNION
SELECT 9 UNION
SELECT 10 UNION
SELECT 11 UNION
SELECT 12 UNION
SELECT 13 UNION
SELECT 14 UNION
SELECT 15 UNION
SELECT 16 UNION
SELECT 17 UNION
SELECT 18 UNION
SELECT 19
)
SELECT H.[Hour],
COUNT(T.BookingID) AS NoOfUsers
FROM H
LEFT JOIN tbl_Visitor T
ON H.[Hour] = DATEPART(HOUR, T.TimeofArrival) AND
((DATEDIFF(dd, T.TimeofArrival, #DateTimeToFilter) = 0) AND (DATEDIFF(mm, T.TimeofArrival, #DateTimeToFilter) = 0) AND
(DATEDIFF(yy, T.TimeofArrival, #DateTimeToFilter) = 0))
GROUP BY H.[Hour];
This forces the data returned for each hour irrespective of whether there is any data or not.
How could I add the half hourly data to be added also, so the returned data look like.
Hour Count
7 0
7.5 0
8 0
8.5 0
9 0
9.5 0
10 4
10.5 0
11 0
11.5 0
12 0
12.5 0
13 0
13.5 0
14 5
14.5 0
15 2
15.5 0
16 2
16.5 0
17 0
17.5 0
18 0
18.5 0
19 0
19.5 0
The data is stored in the database as a smalltimedate, i.e. 2014-06-05 14:00:00
Any help is appreciated.
You can use minutes instead of hours:
with h ([Minute]) as (
select 420 union all
select 450 union all
select 480 union all
select 510 union all
select 540 union all
...
Divide the minutes to get fractional hours:
select h.[Minute] / 60.0 as [Hour], ...
Calculate the start and stop time for the interval to filter the data:
... on T.TimeofArrival >= dateadd(minute, h.[Minute], #DateTimeToFilter) and
T.TimeofArrival < dateadd(minute, h.[Minute] + 30, #DateTimeToFilter)
Below is an example that groups by half-hour intervals and can easily be extended for other intervals. I suggest you avoid applying functions to columns in the WHERE clause as that prevents indexes on those columns from being used efficiently.
DECLARE
#DateTimeToFilter smalldatetime = '2014-06-05'
, #IntervalStartTime time = '07:00:00'
, #IntervalEndTime time = '20:00:00'
, #IntervalMinutes int = 30;
WITH
t4 AS (SELECT n FROM (VALUES(0),(0),(0),(0)) t(n))
, t256 AS (SELECT 0 AS n FROM t4 AS a CROSS JOIN t4 AS b CROSS JOIN t4 AS c CROSS JOIN t4 AS d)
, t64k AS (SELECT ROW_NUMBER() OVER (ORDER BY (a.n)) AS num FROM t256 AS a CROSS JOIN t256 AS b)
, intervals AS (SELECT DATEADD(minute, (num - 1) * #IntervalMinutes, #DateTimeToFilter) AS interval
FROM t64k
WHERE num <= 1440 / #IntervalMinutes)
SELECT
interval
, CAST(DATEDIFF(minute, #DateTimeToFilter, interval) / 60.0 AS decimal(3, 1)) AS Hour
, COUNT(T.BookingID) AS NoOfUsers
FROM intervals
LEFT JOIN dbo.tbl_Visitor T
ON T.TimeofArrival >= intervals.interval
AND T.TimeofArrival < DATEADD(minute, #IntervalMinutes, intervals.interval)
WHERE
interval >= DATEADD(minute, DATEDIFF(minute, '', #IntervalStartTime), #DateTimeToFilter)
AND interval < DATEADD(minute, DATEDIFF(minute, '', #IntervalEndTime), #DateTimeToFilter)
GROUP BY interval
ORDER BY Hour;

How To Group By Only Some Rows

I have some records.
ID Salary WillGroupBy Amount
----------------------------------------
6320 100 1 15
6320 150 1 20
6694 200 0 25
6694 300 0 30
7620 400 1 45
7620 500 1 50
How can I group by only which "WillGroupBy = 1" records?
(I will SUM Salary and Amount columns)
I want to get this result:
ID Salary WillGroupBy Amount
----------------------------------------
6320 250 1 35
6694 200 0 25
6694 300 0 30
7620 900 1 95
Can you help me please :( ?
Solution:
SELECT ID, SUM(Salary) Salary, WillGroupBy, SUM(Amount) Amount
FROM YourTable
where WILLGROUPBY = 0
union all
SELECT ID, SUM(Salary) Salary, WillGroupBy, SUM(Amount) Amount
FROM YourTable
where WILLGROUPBY = 1
group by ID, WillGroupBy
I used this solution via Erhan.
I would to know that how it could be in another way.
With MySQL you can do:
SELECT ID, SUM(Salary) Salary, WillGroupBy, SUM(Amount) Amount, #row := #row + 1
FROM YourTable
JOIN (SELECT #row := 0) v
GROUP BY ID, IF(WillGroupBy = 1, -1, #row)
DEMO

Resources