SQLITE: Calculate price changes with multiple rows in same table - sql

I have a database with the following schema and I need to be able to calculate the difference when a price changes for any given store and include it in the query results. Is it possible with the following query?:
SELECT * from prices
WHERE rowid in (
SELECT p.rowid from prices p
LEFT JOIN prices p1
ON (p1.rowid = (select max(rowid) from prices pp where pp.rowid < p.rowid))
WHERE p.price > p1.price + .01 and p.address = p1.address
)
I would like to print a report that looks similar to this:
Store--------State-------City--------Address-------Price-------Difference
ACME........Ohio........Akron........123 Elm.......10.25......+0.25
ACME........Ohio........Akron........123 hurst.....9.25.......+1.25
ACME........Ohio........Akron........125 Elm.......5.00.......-0.60
Here is a sample of the data that I am working with:
http://www.sqlfiddle.com/#!7/d020f/37/0

If you want to show the change between a price and the one before it you could do that using the insert query. Below I set the price to be 3.64 . In the query I've ordered by row but it would be better to order by date
INSERT INTO PRICES
Select url, state, city, location, 3.64 as price, company, address, '5:45:11 PM 5/30/2014 est.', 3.64 -price as change FROM PRICES
WHERE address = '1670 US-31 N & I-65'
ORDER BY rowid DESC LIMIT 0,1

Related

How to join a query result with a value, received from another query?

I want to calculate transaction costs in USD
for a number of most recent transactions
on the Rootstock blockchain.
I have a PostgreSQL database table with token
prices reports.token_prices
from which I select the value
of the latest available RBTC price in USD:
select tp.price_in_usd
from reports.token_prices tp
where tp.chain_id = 30
and tp.coingecko_token_id = 'rootstock'
order by tp.dt desc
limit 1
(note that tp.dt is a timestamp)
Result of the query:
16995.771
Then I have a table with all transactions,
chain_rsk_mainnet.block_transactions,
from which I select the gas fees
for the 5 most recent ones:
select
bt.fees_paid
from chain_rsk_mainnet.block_transactions bt
order by bt.block_id desc, bt.tx_offset
limit 5
(note that instead of using a timestamp, I'm using bt.block_id and bt.tx_offset for transaction order)
Result:
0
4469416300800
4469416300800
16450260000000
0
Now I want to multiply each of these numbers
by the result of the first query.
How can I do this in SQL?
Without further information your simplest option would be just convert the first query into a CTE then Join that result in the second query.
with price_cte(price_in_usd) as
(select tp.price_in_usd
from reports.token_prices tp
where tp.chain_id = 30
and tp.coingecko_token_id = 'rootstock'
order by tp.dt desc
limit 1
)
select bt.fees_paid * p.price_in_usd) "Fees Paid in USD"
from chain_rsk_mainnet.block_transactions bt
cross join price_cte p
order by bt.block_id desc, bt.tx_offset
limit 5;
NOTE: Not tested, no sample data nor results.

How to select 1000 customers who were the first to gain 1000 bonus points for purchases in categories "Taxi" and "Books"? (SQLite)

The BONUS table has attributes: client_id, bonus_date, the number of accrued bonuses (bonus_cnt), mcc code of the transaction for which added bonuses (mcc_code). The MCC_CATEGORIES table is a mcc code reference.
Attributes:
mcc-code (mcc_code), category (for example, supermarkets, transport, pharmacies, etc., mcc_category)
How to select 1000 customers who were the first to gain 1000 bonus points for purchases in
categories "Taxi" and "Books"?
BONUS table looks like:
CLIENT_ID BONUS_DATE BONUS_CNT MCC_CODE
1121 2020-01-02 23 5432
3421 2020-04-15 7 654
...
MCC_CATEGORIES table looks like:
MCC_CODE MCC_CATEGORY
5432 Taxi
3532 Music
...
I would use window functions and aggregation: first join the tables and compute the running sum of bonus per user and category. Then aggregate by user and category, and get the date when they reached a bonus of 1000. Finally, compute the date when each user reached the target on both categories, order by that, and limit:
select client_id, max(bonus_date) bonus_date
from (
select client_id, mcc_category, min(bonus_date) bonus_date
from (
select b.client_id, b.bonus_date, c.mcc_category,
sum(bonus_cnt) over(partition by b.client_id, c.mcc_category order by b.bonus_date) sum_bonus
from bonus b
inner join mcc_categories c on c.mcc_code = b.mcc_code
where mcc_category in ('Taxi', 'Books')
) t
where sum_bonus >= 1000
group by client_id, mcc_category
) t
group by client_id
having count(*) = 2
order by bonus_date
limit 1000
Window functions are available in SQLite starting version 3.25.
How to select 1000 customers who were the first to gain 1000 bonus points for purchases in categories "Taxi" and "Books"?
I am guessing you want to combine the bonuses for the two categories together. If so:
select client_id, min(bonus_date) as min_bonus_date
from (select b.client_id, b.bonus_date, b.bonus_cnt,
sum(b.bonus_cnt) over (partition by b.client_id order by b.bonus_date) as running_bonus_cnt
from bonus b join
mcc_categories c
on c.mcc_code = b.mcc_code
where mcc_category in ('Taxi', 'Books')
) bc
where running_bonus_cnt >= 1000 and
running_bonus_cnt - bonus_cnt < 1000
group by client_id
order by min_bonus_date
limit 1000;
Note how this works. The subquery calculates the running bonus amount. The where clause then gets the one row where the bonus count first exceeds 1000.
The rest is just aggregation.

Conditional left join on max date and where clause in second table

I am attempting to join a customer table with sales table where I show the list of all customers in database and any paid sale the customer might have in the sales tables. Now a customer can have multiple sales rows in the sales table.
This is an example sales record of one customer with multiple sales in the sale tables
while extracting this record I would like to get only the MAX (q_saledatetime) WHERE the q_paidamount is > 0.
as in show me the last time this customer made a payment to us. So in this case row 2 where they paid 8.90 is what I would like to get for that customer. If a customer has no record in the sales table, show their name/details on the list either way.
My failure at the moment is how to include the where clause of the paid amount + max date column.
ATTEMPT A
select DISTINCT ON (q_customer.q_code)
q_customer.q_code, q_customer.q_name, -- customer info
MAX(q_saleheader.q_saledatetime) AS latestDate, q_saleheader.q_paidamount -- saleheader info
FROM q_customer
LEFT JOIN q_saleheader ON (q_customer.q_code = q_saleheader.q_customercode)
group by q_customer.q_code, q_customer.q_name , q_saleheader.q_saledatetime, q_saleheader.q_paidamount
order by q_customer.q_code ASC
which results in
so for Fred Blogg is picking up details from row 4 instead of 2 (first image). As there's no rule for q_paidamount at this point
ATTEMPT B
SELECT
customer.q_code, customer.q_name, -- customer info
sale.q_saledatetime, sale.q_paidamount -- sale info
FROM q_customer customer
LEFT JOIN (SELECT * FROM q_saleheader WHERE q_saledatetime =
(SELECT MAX(q_saledatetime) FROM q_saleheader b1 where q_paidamount > 0 ))
sale ON sale.q_customercode = customer.q_code
which results in
This doesnt seem to be getting any information from the sale table at all.
Update:
After having a closer look at my first attempt I amended the statement and came up with this solution which achieves the same results as Michal's answer. I just curious to know is there any pitfalls or perfomance disadvantages with the following way.
select DISTINCT ON (q_customer.q_code)
q_customer.q_code, q_customer.q_name, -- customer info
q_saleheader.q_saledatetime, q_saleheader.q_paidamount -- saleheader info
FROM q_customer
LEFT JOIN q_saleheader ON (q_customer.q_code = q_saleheader.q_customercode AND
q_saleheader.q_paidamount > 0 )
group by q_customer.q_code, q_customer.q_name , q_saleheader.q_saledatetime,
q_saleheader.q_paidamount
order by q_customer.q_code ASC, q_saleheader.q_saledatetime DESC
main change was adding AND q_saleheader.q_paidamount > 0 on the join and q_saleheader.q_saledatetime DESC to make sure are getting the top row of that related data. As mentioned both Michal's answer and this solution achieve the same results. Just curious about pitfalls in either of the two ways.
Try this query:
SELECT c.q_code,
c.q_name,
CASE WHEN q_saledatetime <> '1900-01-01 00:00:00.000' THEN q_saledatetime END q_saledatetime,
q_paidamount
FROM (
SELECT c.q_code,
c.q_name,
coalesce(s.q_saledatetime, '1900-01-01 00:00:00.000') q_saledatetime, --it will indicate customer with no data
s.q_paidamount,
ROW_NUMBER() OVER (PARTITION BY c.q_code ORDER BY COALESCE(s.q_saledatetime, '1900-01-01') DESC) rn
FROM q_customer c
LEFT JOIN (SELECT q_saledatetime,
q_paidamount
FROM q_saleheader
WHERE q_paidamount > 0) s
ON c.q_code = s.q_customercode
) c WHERE rn = 1

SELECT TOP 10 rows

I have built an SQL Query that returns me the top 10 customers which have the highest outstanding. The oustanding is on product level (each product has its own outstanding).
Untill now everything works fine, my only problem is that if a certain customer has more then 1 product then the second product or more should be categorized under the same customer_id like in the second picture (because the first product that has the highest outstanding contagions the second product that may have a lower outstanding that the other 9 clients of top 10).
How can I modify my query in order to do that? Is it possible in SQL Server 2012?
My query is:
select top 10 CUSTOMER_ID
,S90T01_GROSS_EXPOSURE_THSD_EUR
,S90T01_COGNOS_PROD_NAME
,S90T01_DPD_C
,PREVIOUS_BUCKET_DPD_REP
,S90T01_BUCKET_DPD_REP
from [dbo].[DM_07MONTHLY_DATA]
where S90T01_CLIENT_SEGMENT = 'PI'
and YYYY_MM = '2017_01'
group by CUSTOMER_ID
,S90T01_GROSS_EXPOSURE_THSD_EUR
,S90T01_COGNOS_PROD_NAME
,S90T01_DPD_C
,PREVIOUS_BUCKET_DPD_REP
,S90T01_BUCKET_DPD_REP
order by S90T01_GROSS_EXPOSURE_THSD_EUR desc;
You need to calculate the top Customers first, then pull out all their products. You can do this with a Common Table Expression.
As you haven't provided any test data this is untested, but I think it will work for you:
with top10 as
(
select top 10 CUSTOMER_ID
,sum(S90T01_GROSS_EXPOSURE_THSD_EUR) as TOTAL_S90T01_GROSS_EXPOSURE_THSD_EUR
from [dbo].[DM_07MONTHLY_DATA]
where S90T01_CLIENT_SEGMENT = 'PI'
and YYYY_MM = '2017_01'
group by CUSTOMER_ID
order by TOTAL_S90T01_GROSS_EXPOSURE_THSD_EUR desc
)
select m.CUSTOMER_ID
,m.S90T01_GROSS_EXPOSURE_THSD_EUR
,m.S90T01_COGNOS_PROD_NAME
,m.S90T01_DPD_C
,m.PREVIOUS_BUCKET_DPD_REP
,m.S90T01_BUCKET_DPD_REP
from [dbo].[DM_07MONTHLY_DATA] m
join top10 t
on m.CUSTOMER_ID = t.CUSTOMER_ID
order by t.TOTAL_S90T01_GROSS_EXPOSURE_THSD_EUR desc
,m.S90T01_GROSS_EXPOSURE_THSD_EUR;

SQL Divide one count result by another OR Alternate solution

I have a table with the following schema.
Relational Database Schema:
Hotel = hotelNo, hotelName, city
Room = roomNo, hotelNo(FK), type, rate
Guest = guestNo, guestName, guestAddress
Booking = hotelNo(FK), guestNo(FK), dateFrom, dateTo, roomNo(FK)
There are entries in each table however their data isn't completely relevant to this question.
I need to calculate the average number of booking made for each hotel, ensuring that I include the hotels which do not currently have bookings.
I have this :
-- Call this select 1
select count(*)
from booking b, hotel h
where b.hotelNo=h.hotelNo;
-- Call this select 2
select count(*)
from hotel;
Select 1 returns the total number of bookings. Select 2 returns the total number of hotels. If I could simply divide the output of count in select 1 by the output of count in select 2 I would have my answer.
If this is possible can someone please help me with the code, otherwise can someone think of an alternate solution to achieve the same result?
If by "average number of bookings", you just want to divide the two numbers, then you can do:
select count(b.HotelNo) / count(distinct h.hotelNo)
from hotel h left join
booking b
on h.hotelNo = b.hotelNo;
Create a view say temp to get count of rooms per hotel[ The data you 'see' in a view, is not actually stored anywhere, and is generated from the tables on the fly.]
create view temp
as select hotelNo,count(*) as cnt from room group by hotelNo;
Use following query to fetch avg.
select booking.hotelNo,count(*) / cnt
from booking ,temp
where booking.hotelNo = temp.hotelNo
group by booking.hotelNo;
or
select booking.hotelNo,count(*) / cnt
from booking
INNER JOIN temp
on booking.hotelNo = temp.hotelNo
group by booking.hotelNo;
This will not include hotels that do not have any booking.