sqllite select rows until a total amount is met in a column - sqlite

I've seen the similar problem with mysql, but I barely could find any solution for the problem with sqllite.
My sample table,
-----------------------------
ID | Product Name | Price
-----------------------------
1 A 2
2 B 2
3 C 1
4 D 3
5 E 2
Here I need to get the rows until the total for the price column is equal or smaller than 5 in ascending order.

You could do a Running total using the Product ID and ORDER BY Product ID like the one below:
SELECT p1.ID, p1.ProductName, p1.Price,
(SELECT SUM(p2.Price) FROM Products p2 WHERE p1.ID >= p2.ID ORDER BY p2.ID ) as RunningTotal
FROM Products p1
WHERE RunningTotal <= 5
ORDER BY p1.ID
See Fiddle Demo
Or using the Price and ORDER BY Price like one below:
SELECT p1.ID, p1.ProductName, p1.Price,
(SELECT SUM(p2.Price) FROM Products p2 WHERE p1.Price >= p2.Price ORDER BY Price )
as RunningTotal
FROM Products p1
WHERE RunningTotal <= 5
ORDER BY p1.Price;
See 2nd Fiddle Demo

It's probably best to do it in code as SQLite does not support an easy way to do cumulative sums as far as I know. You can create an index on the Price column.
Then running a query like
SELECT * FROM <table> ORDER BY Price
Note that this will not eagerly fetch all rows from the database, but just provide you with the cursor. Keep fetching the next row from the cursor until you reach the desired sum.

Related

SQLite - Joining 2 tables excluding certain rows based on a partial string match

Imagine I have two tables:
Table A
Names
Sales
Department
Dave
5
Shoes
mike
6
Apparel
Dan
7
Front End
Table B
Names
SALES
Department
Dave
5
Shoes
mike
12
Apparel
Dan
7
Front End
Gregg
23
Shoes
Kim
15
Front End
I want to create a query that joins the tables by names and separates sum of sales by table. I additionally want to filter my query to remove string matches or partial matches in this case by certain names.
What I want is the following result
Table C:
A Sales Sum
B Sales Sum
18
24
I know I can do this with a query like the following:
SELECT SUM(A.sales) AS 'A Sales Sum', SUM(B.sales) AS 'B sales Sum' FROM A
JOIN B
ON B.names = A.Names
WHERE Names NOT LIKE '%Gregg%' OR NOT LIKE '%Kim%'
The problem with this is the WHERE clause doesn't seem to apply, or applies to the wrong table. Since the Names column doesn't exactly match between the two, what I think is happening is when they are joined 'ON B.names = A.Names', the extras from B are being excluded? When I flip things around though I get the same result, which is no filter being applied. The wrong result I am getting is the following:
Table D:
A Sales Sum
B Sales Sum
18
62
Clearly I have a syntax issue here since I'm pretty new to SQL. What am I missing? Thanks!
You don't need a join or a union of the tables and you shouldn't do it.
Aggregate in each table separately and return the results with 2 subqueries:
SELECT
(SELECT SUM(Sales) FROM A WHERE Names NOT LIKE '%Gregg%' AND Names NOT LIKE '%Kim%') ASalesSum,
(SELECT SUM(Sales) FROM B WHERE Names NOT LIKE '%Gregg%' AND Names NOT LIKE '%Kim%') BSalesSum
I think you want a union approach here:
SELECT
SUM(CASE WHEN src = 'A' THEN sales ELSE 0 END) AS "A Sales Sum",
SUM(CASE WHEN src = 'B' THEN sales ELSE 0 END) AS "B Sales Sum"
FROM
(
SELECT sales, 'A' AS src FROM A WHERE Names NOT IN ('Gregg', 'Kim')
UNION ALL
SELECT sales, 'B' FROM B WHERE Names NOT IN ('Gregg', 'Kim')
) t;
Here is a demo showing that the above query is working.

Count in group_concat

I have this situation in Mysql table.
-----------------
code gr. state
-----------------
10 a available
10 a sold
10 b available
10 a available
10 a sold
10 a printed
10 b available
10 b sold
10 b available
------------------
I need to group these data for group getting something like
group a -> available(3), sold(2), printed(1)
group b -> available(2), sold(1), printed(0)
I tried combining group_concat() and count() but can't get the result I need.
My goal is to have 1 single row per group (group by is ok)
The states are always these 3 (available, sold, printed)
thx for help
SUM with IF could give you the right answear.
SELECT gr,
sum(if(state,'available',1,0)) available,
sum(if(state,'sold',1,0)) sold,
sum(if(state,'printed',1,0)) printed
FROM table
GROUP BY gr

Correct history data

I have a scenario where i have to correct the history data. The current data is like below:
Status_cd event_id phase_cd start_dt end_dt
110 23456 30 1/1/2017 ?
110 23456 31 1/2/2017 ?
Status_cd event_id phase_cd start_dt end_dt
110 23456 30 1/1/2017 ?
111 23456 30 1/2/2017 ?
The major columns are status_cd and phase_cd. So, if any one of them change the history should be handled with the start dt of the next record as the end date of the previous record.
Here both the records are open which is not correct.
Please suggest on how to handle both the scenarios.
Thanks.
How are your history rows ordered in the table? In other words, how do you decide which history rows to compare to see if a value was changed? And how do you uniquely identify a history row entry?
If you order your history rows by start_dt, for example, you can compare the previous and current row values using window functions, like Rob suggested:
UPDATE MyHistoryTable
FROM (
-- Get source history rows that need to be updated
SELECT
history_row_id, -- Change this field to match your table
MAX(status_cd) OVER(ORDER BY start_dt ROWS BETWEEN 1 FOLLOWING AND 1 FOLLOWING) AS status_cd_next, -- Get "status_cd" value for "next" history row
MAX(phase_cd) OVER(ORDER BY start_dt ROWS BETWEEN 1 FOLLOWING AND 1 FOLLOWING) AS phase_cd_next,
MAX(start_dt) OVER(ORDER BY start_dt ROWS BETWEEN 1 FOLLOWING AND 1 FOLLOWING) AS start_dt_next
FROM MyHistoryTable
WHERE status_cd <> status_cd_next -- Check "status_cd" values are different
OR phase_cd <> phase_cd_next -- Check "phase_cd" values are different
) src
SET MyHistoryTable.end_dt = src.start_dt_next -- Update "end_dt" value of current history row to be "start_dt" value of next history row
WHERE MyHistoryTable.history_row_id = src.history_row_id -- Match source rows to target rows
This assumes you have a column to uniquely identify each history row, called "history_row_id". Give it a try and let me know.
I don't have a TD system to test on, so you may need to futz with the table aliases too. You'll also probably need to handle the edge cases (i.e. first/last rows in the table).

SQL Server - Group by, having and count in a mix

I have a database with a long list of records. Most of the columns have foreign keys to other tables.
Example:
ID SectorId BranchId
-- -------- --------
5 3 5
And then I will have a table with sectors, branches ect.
My issue:
I want to know how many records which has sector 1, 2, 3 ... n. So what I want is a group by Sector and then some count(*) which will tell me how many there is of each.
Expected output
So for instance, if I have 20 records the result might look like this:
SectorId Count
-------- -----
1 3
2 10
3 4
4 6
My attempts so far
I do not normally work a lot with databases and I have been trying to solve this for 1.5 hours. I have tried something like this:
SELECT COUNT(*)
FROM Records r
GROUP BY r.Sector
WHERE r.Date BETWEEN '2011-01-01' AND '2011-12-31'
But... errors and problems all over!
I would really appreciate some help. I do know this is probably very simple.
Thanks!
The sequence of your query is not correct; it should be like this: -
SELECT COUNT(*)
FROM Records r
WHERE r.Date BETWEEN '2011-01-01' AND '2011-12-31'
GROUP BY r.Sector
The output will be only counts i.e.
count
-----
3
10
4
6
If you want to fetch both sector and count then you need to modify the query a little
SELECT r.Sector, COUNT(*) as Count
FROM Records r
WHERE r.Date BETWEEN '2011-01-01' AND '2011-12-31'
GROUP BY r.Sector
The output will be like this: -
Sector Count
------ -----
1 3
2 10
3 4
3 6
Your query was partially right,But it needs some modification.
If I write this way:-
SELECT r.SectorID,COUNT(*) AS count
FROM Records r
WHERE r.Date BETWEEN '2011-01-01' AND '2011-12-31'
GROUP BY r.SectorID
Then output will be:-
SectorID Count
1 3
2 10
3 4
4 6

Get Correct Price based on Effectivity Date

I have a problem getting the right "Price" for a product based on Effectivity date.
Example, I have 2 tables:
a. "Transaction" table --> this contains the products ordered, and
b. "Item Master" table --> this contains the product prices and effectivity dates of those prices
Inside the Trasaction table:
INVOICE_NO INVOICE_DATE PRODUCT_PKG_CODE PRODUCT_PKG_ITEM
1234 6/29/2009 ProductA ProductA-01
1234 6/29/2009 ProductA ProductA-02
1234 6/29/2009 ProductA ProductA-03
Inside the "Item_Master" table:
PRODUCT_PKG_CODE PRODUCT_PKG_ITEM PRODUCT_ITEM_PRICE EFFECTIVITY_DATE
ProductA ProductA-01 25 6/1/2009
ProductA ProductA-02 22 6/1/2009
ProductA ProductA-03 20 6/1/2009
ProductA ProductA-01 15 5/1/2009
ProductA ProductA-02 12 5/1/2009
ProductA ProductA-03 10 5/1/2009
ProductA ProductA-01 19 4/1/2009
ProductA ProductA-02 17 4/1/2009
ProductA ProductA-03 15 4/1/2009
In my report, I need to display the Invoices and Orders,
as well as the Price of the Order Item which was effective
at the time it was paid (Invoice Date).
My query looks like this (my source db is Oracle):
SELECT T.INVOICE_NO,
T.INVOICE_DATE,
T.PRODUCT_PKG_CODE,
T.PRODUCT_PKG_ITEM,
P.PRODUCT_ITEM_PRICE FROM TRANSACTION T,
ITEM_MASTER P WHERE T.PRODUCT_PKG_CODE = P.PRODUCT_PKG_CODE
AND T.PRODUCT_PKG_ITEM = P.PRODUCT_PKG_ITEM
AND P.EFFECTIVITY_DATE <= T.INVOICE_DATE
AND T.INVOICE_NO = '1234';
...which shows 2 prices for each item.
I did some other different query styles
but to no avail, so I decided
it's time to get help. :)
Thanks to any of you who can
share your knowledge. --CJ--
p.s. Sorry, my post doesn't even look right! :D
If it's returning two rows with different effective dates that are less than the invoice date, you may want to change your date join to
'AND T.INVOICE_DATE = (
select max(effectivity_date)
from item_master
where effectivity_date < t.invoice_date)'
or something like that, to only get the one price that is the most recent one before the invoice date.
Analytics is your friend. You can use the FIRST_VALUE() function, for example, to get all the product_item_prices for the given product, sort by effectivity_date (descending), and just pick the first one. You'll need a DISTINCT as well so that only one row is returned for each transaction.
SELECT DISTINCT
T.INVOICE_NO,
T.INVOICE_DATE,
T.PRODUCT_PKG_CODE,
T.PRODUCT_PKG_ITEM,
FIRST_VALUE(P.PRODUCT_ITEM_PRICE)
OVER (PARTITION BY T.INVOICE_NO, T.INVOICE_DATE,
T.PRODUCT_PKG_CODE, T.PRODUCT_PKG_ITEM
ORDER BY P.EFFECTIVITY_DATE DESC)
as PRODUCT_ITEM_PRICE
FROM TRANSACTION T,
ITEM_MASTER P
WHERE T.PRODUCT_PKG_CODE = P.PRODUCT_PKG_CODE
AND T.PRODUCT_PKG_ITEM = P.PRODUCT_PKG_ITEM
AND P.EFFECTIVITY_DATE <= T.INVOICE_DATE
AND T.INVOICE_NO = '1234';
While your question's formatting is a bit too messy for me to get all the details, it sure does look like you're looking for the standard SQL construct ROW_NUMBER() OVER with both PARTITION and ORDER_BY -- it's in PostgreSql 8.4 and has been in Oracle [and MS SQL Server too, and DB2...] for quite a while, and it's the handiest way to select the "top" (or "top N") "by group" and with a certain order of anything in a SQL query. Look it up, see here for the PosgreSQL-specific docs.

Resources