MySQL query is not coming back - performance enhancements? - mysql

I have a shopware database which has the structure:
s_articles: all products
id | name | mode
s_order: all orders
id | ordertime | userId | status
s_order_details: all line items for an order
id | orderId | articleId
Now I want to achieve the following: I want to now how many users bought a product in the last 90 days which have bought the same product also earlier than the last 90 days.
I do it like this: I get all articles, then I get count all customers who have bought the article before the last 90 days "boughtBefore90", then I have another sub-select with another sub-select where I collect all the users who have bought the product in the last 90 days and have bought it earlier than that as well and save it in "boughtLast90".
Then I calcucate the retention rate. This works well with one product, but not with the whole articles database. Is there any way to write this sql with better performance in mind?
My query looks like this:
select articleId, articleName, boughtBefore90, boughtLast90, boughtLast90/boughtBefore90 as 'retention'
FROM
(select
s_articles.id as 'articleId',
s_articles.name as 'articleName',
(select count(*)
FROM s_order_details as s_order_details_2
join s_order as s_order_2 on s_order_2.Id = s_order_details_2.orderId
where s_order_details_2.articleId = s_articles.id
and (s_order_2.status = 0 or s_order_2.status = 2)
and s_order_2.ordertime > '2017-01-01 00:00:00'
and s_order_2.ordertime < '2017-03-09 23:59:59'
) as 'boughtBefore90',
(select count(*) from s_order_details as s_order_details_3
join s_order as s_order_3 on s_order_3.id = s_order_details_3.orderId
where s_order_3.ordertime > '2017-03-10 00:00:00'
and s_order_3.ordertime < '2017-06-07 23:59:59'
and s_order_details_3.articleId = s_articles.id
and (s_order_3.status = 0 or s_order_3.status = 2)
AND s_order_3.userId = (
select DISTINCT(s_order_4.userId) from s_order_details as s_order_details_4
join s_order as s_order_4 on s_order_4.id = s_order_details_4.orderId
where s_order_4.ordertime > '2017-01-01 00:00:00'
and s_order_4.ordertime < '2017-03-09 23:59:59'
and s_order_details_4.articleId = s_articles.id
and s_order_3.userId = s_order_4.userId
and (s_order_4.status = 0 or s_order_4.status = 2))
) as 'boughtLast90'
from
s_articles
join s_order_details on s_order_details.articleID = s_articles.id
join s_order on s_order.id = s_order_details.orderId
WHERE s_articles.mode = 0
AND s_order_details.modus = 0
AND (s_order.status = 2 or s_order.status = 0)
group by s_articles.id) as resulttable;
Thank you!

This seems like a pretty simple query. Here is the list of customers and articles:
select o.customerid, od.articleid
from s_orders o join
s_order_details od
on od.orderid = o.id
group by o.customerid, od.articleid
having max(o.ordertime) > curdate() - interval 90 days and
min(o.ordertime) <= curdate() - interval 90 days;
If you want a count, use this as a subquery and use either count(*) (for customer/article pairs) or count(distinct customerid) (for the number of customers).

Related

Subtracting or Adding data based on logtime of another table

So currently I have 2 tables called listings and logs table. The listings table holds a products reference number and it's current status. So suppose if it's status was Publish currently and it's sold, the status updates to Sold. Here the refno. in this table is unique since the status can change for 1 product.
Now I have another table called Logs table, this table records all the status changes that have happened for a particular product(referenced by refno) in a particular timeframe. Suppose the Product with refno. 5 was Publish on 1st October and Sold on 2nd October, The logs table will display as:
Refno
status_from
status_to
logtime
5
Stock
Publish
2021-10-01
5
Publish
Sold
2021-10-02
This is how my tables currently look like:
Listings table:('D'=>'Draft','N'=>'Action','Y'=>'Publish')
Logs Table which I'm getting using the following statement:
SELECT refno, logtime, status_from, status_to FROM (
SELECT refno, logtime, status_from, status_to, ROW_NUMBER() OVER(PARTITION BY refno ORDER BY logtime DESC)
AS RN FROM crm_logs WHERE logtime < '2021-10-12 00:00:00' ) r
WHERE r.RN = 1 UNION SELECT refno, logtime, status_from, status_to
FROM crm_logs WHERE logtime <= '2021-10-12 00:00:00' AND logtime >= '2015-10-02 00:00:00'
ORDER BY `refno` ASC
The logs table makes a new record every status change made and passes the current timestamp as the logtime, and the listings table changes/updates the status and updates its update_date. Now to get the total listings as of today I'm using the following statement:
SELECT SUM(status_to = 'D') AS draft, SUM(status_to = 'N') AS action, SUM(status_to = 'Y') AS publish FROM `crm_listings`
And this returns all the count data for status as of the current day.
Now this is where it gets confusing for me. So suppose today the count under action is 10 and tomorrow it'll be 15, and I want to retrieve the total that was present yesterday(10). So for this what I would've to do is take todays total(15) and subtract all the places where a product was changed to draft in between yesterday and today(Total count today in listing table - count(*) where status_to='Action' from logs table). Or vice versa, if yesterday it was 10 under action and today it is 5, it should add the values from the status_from column in logs table
Note: Refno isn't unique in my logs table since a product with the same refno can be marked as publish 1 day and unpublish another, but it is unique in my listings table.
Link to dbfiddle: https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=01cb3ccfda09f6ddbbbaf02ec92ca894
I am sure it can be simplifed or better. But its my query and logic :
I found status_changes per refno's and calculated total changes from the desired day to present :
select status_logs, sum(cnt_status) to_add from (
SELECT
status_to as status_logs, -1*count(*) cnt_status
FROM logs lm
where
id = (select max(id) from logs l where l.refno = lm.refno) and
logtime >= '2021-10-01 00:00:00'
group by status_to
union all
SELECT
status_from, count(*) cnt_status_from
FROM logs lm
where
id = (select max(id) from logs l where l.refno = lm.refno) and
logtime >= '2021-10-01 00:00:00'
group by status_from ) total_changes
group by status_logs
I matched the keys between listings table and logs table by converting listings table keys :
select
case status
when 'D' THEN 'Draft'
when 'A' THEN 'Action'
when 'Y' THEN 'Publish'
when 'S' THEN 'Sold'
when 'N' THEN 'Let'
END status_l ,COUNT(*) c
from listings
group by status
I joined them and add the calculations to total sum of current data.
I had to use full outer join , so i have one left and one right join with the same subqueries.
Lastly I used distinct , since it will generate same result for each joined query and used ifnull to bring the other tables status to the other column .
select distinct IFNULL(status_l, status_logs) status, counts_at_2021_10_01
from (select l.*,
logs.*,
l.c + ifnull(logs.to_add, 0) counts_at_2021_10_01
from (select case status
when 'D' THEN
'Draft'
when 'A' THEN
'Action'
when 'Y' THEN
'Publish'
when 'S' THEN
'Sold'
when 'N' THEN
'Let'
END status_l,
COUNT(*) c
from listings
group by status) l
left join (
select status_logs, sum(cnt_status) to_add
from (SELECT status_to as status_logs,
-1 * count(*) cnt_status
FROM logs lm
where id = (select max(id)
from logs l
where l.refno = lm.refno)
and logtime >= '2021-10-01 00:00:00'
group by status_to
union all
SELECT status_from, count(*) cnt_status_from
FROM logs lm
where id = (select max(id)
from logs l
where l.refno = lm.refno)
and logtime >= '2021-10-01 00:00:00'
group by status_from) total_changes
group by status_logs) logs
on logs.status_logs = l.status_l
union all
select l.*,
logs.*,
l.c + ifnull(logs.to_add, 0) counts_at_2021_05_01
from (select case status
when 'D' THEN
'Draft'
when 'A' THEN
'Action'
when 'Y' THEN
'Publish'
when 'S' THEN
'Sold'
when 'N' THEN
'Let'
END status_l,
COUNT(*) c
from listings
group by status) l
right join (
select status_logs, sum(cnt_status) to_add
from (SELECT status_to as status_logs,
-1 * count(*) cnt_status
FROM logs lm
where id = (select max(id)
from logs l
where l.refno = lm.refno)
and logtime >= '2021-10-01 00:00:00'
group by status_to
union all
SELECT status_from, count(*) cnt_status_from
FROM logs lm
where id = (select max(id)
from logs l
where l.refno = lm.refno)
and logtime >= '2021-10-01 00:00:00'
group by status_from) total_changes
group by status_logs) logs
on logs.status_logs = l.status_l) l

How to group by date

Below I cannot do Group by Date the following figures.
I have tried to put Group By in different lines, but not working.
SELECT SUM(a.NetAmount) AS TotalDonation
FROM (
SELECT
(
CASE WHEN bt.BalanceTransactionCurrencyID = 17
THEN bt.BalanceTransactionNet
ELSE
bt.BalanceTransactionNet * (SELECT TOP 1 ExrateValue FROM Exrate WHERE ExrateDate < bt.BalanceTransactionCreated AND bt.BalanceTransactionCurrencyID = CurrencyID ORDER BY ExrateDate Desc)
END
) AS NetAmount
FROM Charge as ch
JOIN BalanceTransaction as bt ON (ch.BalanceTransactionID = bt.BalanceTransactionID)
WHERE ch.ChargeCreatedDate BETWEEN '3-1-2019' AND '3-31-2019'
) AS a
I wanted to see:
Days Total Amount
March 1 xxxx
March 2 xxxx
March 3 xxx
MySQL does not use TOP. Use LIMIT:
SELECT ChargeCreatedDate, SUM(netamount)
FROM (SELECT ch.ChargeCreatedDate,
(CASE WHEN bt.BalanceTransactionCurrencyID = 17
THEN bt.BalanceTransactionNet
ELSE bt.BalanceTransactionNet * (SELECT e.ExrateValue
FROM Exrate e
WHERE e.ExrateDate < bt.BalanceTransactionCreated AND
e.CurrencyID = bt.BalanceTransactionCurrencyID
ORDER BY ExrateDate Desc
LIMIT 1
)
END) AS NetAmount
FROM Charge ch JOIN
BalanceTransaction bt
ON ch.BalanceTransactionID = bt.BalanceTransactionID
WHERE ch.ChargeCreatedDate BETWEEN '2019-03-01' AND '2019-03-31'
) chtbt
GROUP BY ChargeCreatedDate;
If you happen to be using SQL Server, you can replace the LIMIT 1 with FETCH FIRST 1 ROW ONLY.
The following code will hopefully display what you are looking for
SELECT a.Days AS Days, SUM(cast(a.NetAmount as decimal(16,9))) AS TotalDonation
FROM (
SELECT
(
CASE WHEN bt.BalanceTransactionCurrencyID = 17
THEN bt.BalanceTransactionNet
ELSE
bt.BalanceTransactionNet * (SELECT TOP 1 ExrateValue FROM Exrate WHERE ExrateDate < bt.BalanceTransactionCreated AND bt.BalanceTransactionCurrencyID = CurrencyID ORDER BY ExrateDate Desc)
END
) AS NetAmount,
ch.ChargeCreatedDate as Days
FROM Charge as ch
JOIN BalanceTransaction as bt ON (ch.BalanceTransactionID = bt.BalanceTransactionID)
WHERE ch.ChargeCreatedDate BETWEEN '3-1-2019' AND '3-31-2019'
) AS a GROUP BY a.Days
This should be sufficient. You need to SELECT the desired value in the query in order to get it to show up. Also when using the SUM() function you need to specify what the group value will be.

MYSQL Query with Multiple Selects from Same Table

Getting an error
Operand should contain 1 column(s)
PK is ID
The table just dumps data in to the table
need to get the earliest date qty and the latest date qty and display on the same column
Any help appreciated
SELECT ebx_r_history.ItemNumber,
(SELECT r.QuantitySold as newqty, r.lastupdate as lu
FROM ebx_r_history r
WHERE ebx_r_history.ItemNumber = r.ItemNumber AND ebx_r_history.SKU = r.SKU
ORDER BY r.LastUpdate ASC
LIMIT 1),
(SELECT r.QuantitySold as newqty, r.lastupdate as lu
FROM ebx_r_history r
WHERE ebx_r_history.ItemNumber = r.ItemNumber AND ebx_r_history.SKU = r.SKU
ORDER BY r.LastUpdate DESC
LIMIT 1)
FROM
ebx_r_history
GROUP BY ebx_r_history.ItemNumber,
ebx_r_history.SKU
ORDER BY ebx_r_history.LastUpdate
This version may offer a simplified and faster alternative for you. The inner query for "AllItems" does both a min and max of the last update on a per-item number/sku basis, although I believe they would be one-in-the-same record.
So now, join that results back to the history data by item/sku and only those that match either the min or max date. If a true date/time, there would expect to only be one anyhow, vs just a date-only. So, since there would be 2 possible records (one for the min, one for the max), I am applying a MAX( IIF( )) for each respective matching the minimum and maximum dates respectively and must retain the group by clause.
Note, if you are dealing with date-only entries, or possibilities of the exact same item/sku and lastupdate are the same to the second, then you would need an approach more towards limit 1 per ascending/descending basis.
SELECT
AllItems.ItemNumber,
AllItems.SKU,
AllItems.MinUpdate,
MAX( IIF( rh.lastupdate = AllItems.MinUpdate, rh.Quantity.Sold, 0 )) as QtyAtMinDate,
AllItems.MaxUpdate,
MAX( IIF( rh.lastupdate = AllItems.MaxUpdate, rh.Quantity.Sold, 0 )) as QtyAtMaxDate
from
( SELECT
r.ItemNumber,
r.SKU,
MIN( r.lastupdate ) as MinUpdate,
MAX( r.lastupdate ) as MaxUpdate
FROM
ebx_r_history r
group by
r.ItemNumber,
r.SKU ) AllItems
JOIN ebx_r_history rh
ON AllItems.ItemNumber = rh.ItemNumber
AND AllItems.SKU = rh.SKU
AND ( rh.lastUpdate = AllItems.MinUpdate
OR rh.lastUpdate = AllItems.MaxUpdate )
group by
AllItems.ItemNumber,
AllItems.SKU
Per another answer where you were only looking to IGNORE items within the most recent 14 days, you can just add a WHERE clause to the inner query similar via
WHERE r.LastUpdate >= CURDATE() - INTERVAL 14 DAY
If your history table has an auto-incrementing ID column, AND the respective transactions have the lastUpdate sequentially stamped, such as when they are added and not modified by any other operation, then you could just apply similar but MIN/MAX of the ID column, then join back TWICE on the ID and just each row ONCE such as...
SELECT
AllItems.ItemNumber,
AllItems.SKU,
rhMin.LastUpdate as MinUpdate,
rhMin.QuantitySold as MinSold,
rhMax.LastUpdate as MaxUpdate,
rhMax.QuantitySold as MaxSold
from
( SELECT
r.ItemNumber,
r.SKU,
MIN( r.AutoIncrementColumn ) as MinAutoID,
MAX( r.AutoIncrementColumn ) as MaxAutoID
FROM
ebx_r_history r
group by
r.ItemNumber,
r.SKU ) AllItems
JOIN ebx_r_history rhMin
ON AllItems.MinAutoID = rhMin.AutoIncrementColumn
JOIN ebx_r_history rhMax
ON AllItems.MaxAutoID = rhMax.AutoIncrementColumn
order by
rhMax.LastUpdated
Try something like this:
SELECT r1.ItemNumber,
(
SELECT r.QuantitySold
FROM ebx_r_history r
WHERE r1.ItemNumber = r.ItemNumber
AND r1.SKU = r.SKU
ORDER BY r.LastUpdate ASC LIMIT 1
) AS earliestDateQty,
(
SELECT r.QuantitySold
FROM ebx_r_history r
WHERE r1.ItemNumber = r.ItemNumber
AND r1.SKU = r.SKU
ORDER BY r.LastUpdate DESC LIMIT 1
) AS latestDateQty
FROM ebx_r_history r1
GROUP BY r1.ItemNumber,r1.SKU
ORDER BY 3
You had a couple of errors. you were getting two columns inside the inner selects, and you had a couple of places where you might get the error for ambiguous column name.
sqlFiddle here
SELECT T1.ItemNumber,
T1.SKU,
T1.Old_QuantitySold,
T1.Old_LastUpdate,
T2.New_QuantitySold,
T2.New_LastUpdate
FROM
(SELECT itemNumber,SKU,QuantitySold as Old_QuantitySold,LastUpdate as Old_LastUpdate
FROM ebx_r_history r
WHERE NOT EXISTS (SELECT 1 FROM ebx_r_history e
WHERE e.itemNumber = r.itemNumber AND e.SKU = r.SKU
AND e.LastUpdate < r.LastUpdate)
)T1
LEFT JOIN
(SELECT itemNumber,SKU,QuantitySold as New_QuantitySold,LastUpdate as New_LastUpdate
FROM ebx_r_history r
WHERE NOT EXISTS (SELECT 1 FROM ebx_r_history e
WHERE e.itemNumber = r.itemNumber AND e.SKU = r.SKU
AND e.LastUpdate > r.LastUpdate)
)T2 ON (T2.itemNumber = T1.itemNumber AND T2.SKU = T1.SKU)
WHERE T1.Old_LastUpdate >= CURDATE() - INTERVAL 14 DAY
AND T2.New_LastUpdate >= CURDATE() - INTERVAL 14 DAY
ORDER BY T2.New_LastUpdate;
you can do left join or inner join it's up to you, since T1 will always get earliest records and T2 will always get latest records for the ItemNumber,SKU grouping.
UPDATED TO IGNORE DATA OLDER THAN 14 DAYS
SELECT T1.ItemNumber,
T1.SKU,
T1.Old_QuantitySold,
T1.Old_LastUpdate,
T2.New_QuantitySold,
T2.New_LastUpdate
FROM
(SELECT itemNumber,SKU,QuantitySold as Old_QuantitySold,LastUpdate as Old_LastUpdate
FROM ebx_r_history r
WHERE LastUpdate >= CURDATE() - INTERVAL 14 DAY
AND NOT EXISTS (SELECT 1 FROM ebx_r_history e
WHERE e.itemNumber = r.itemNumber AND e.SKU = r.SKU
AND e.LastUpdate >= CURDATE() - INTERVAL 14 DAY
AND e.LastUpdate < r.LastUpdate)
)T1
LEFT JOIN
(SELECT itemNumber,SKU,QuantitySold as New_QuantitySold,LastUpdate as New_LastUpdate
FROM ebx_r_history r
WHERE LastUpdate >= CURDATE() - INTERVAL 14 DAY
AND NOT EXISTS (SELECT 1 FROM ebx_r_history e
WHERE e.itemNumber = r.itemNumber AND e.SKU = r.SKU
AND e.LastUpdate >= CURDATE() - INTERVAL 14 DAY
AND e.LastUpdate > r.LastUpdate)
)T2 ON (T2.itemNumber = T1.itemNumber AND T2.SKU = T1.SKU)
ORDER BY T2.New_LastUpdate;
ignore data older than 14 days sqlFiddle here
If you want to use exact time (14 days ago), you can replace occurences of CURDATE() with NOW()

JIRA : Issue status count for the past x (i.e 30 ) days

With below Query I able to see the count(no) of issues for all issueType in JIRA for a given date .
ie.
SELECT count(*), STEP.STEP_ID
FROM (SELECT STEP_ID, ENTRY_ID
FROM OS_CURRENTSTEP
WHERE OS_CURRENTSTEP.START_DATE < '<your date>'
UNION SELECT STEP_ID, ENTRY_ID
FROM OS_HISTORYSTEP
WHERE OS_HISTORYSTEP.START_DATE < '<your date>'
AND OS_HISTORYSTEP.FINISH_DATE > '<your date>' ) As STEP,
(SELECT changeitem.OLDVALUE AS VAL, changegroup.ISSUEID AS ISSID
FROM changegroup, changeitem
WHERE changeitem.FIELD = 'Workflow'
AND changeitem.GROUPID = changegroup.ID
UNION SELECT jiraissue.WORKFLOW_ID AS VAL, jiraissue.id as ISSID
FROM jiraissue) As VALID,
jiraissue as JI
WHERE STEP.ENTRY_ID = VALID.VAL
AND VALID.ISSID = JI.id
AND JI.project = <proj_id>
Group By STEP.STEP_ID;
the result is
Status Count
open 12
closed 13
..... ....
What I'd like to achieve is something like this actually ..where the total count for status open and closed for each day .
Date COUNT(Open) COUNT(Closed)
12-1-2012 12 1
13-1-2012 14 5
The general strategy would be this:
Select from a table of all the days in a month
LEFT OUTER JOIN your table that gets counts for each day
(left outer join being necessary in case there were no entries for that day, you'd want it to show a zero value).
So I think this is roughly what you need (not complete and date-function syntax is probably wrong for your db, but it will get you closer):
SELECT aDate
, COALESCE(SUM(CASE WHEN IssueStatus = 'whateverMeansOpen' THEN 1 END,0)) OpenCount
, COALESCE(SUM(CASE WHEN IssueStatus = 'whateverMeansClosed' THEN 1 END,0)) ClosedCount
FROM
(
SELECT DATEADD(DAY, I, #START_DATE) aDate
FROM
(
SELECT number AS I FROM [SomeTableWithAtLeast31Rows]
where number between 1 and 31
) Numbers
WHERE DATEADD(DAY, I, #START_DATE) < #END_DATE
) DateTimesInInterval
LEFT OUTER JOIN
(
Put your query here. It needs to output two columns, DateTimeOfIssue and IssueStatus
) yourHugeQuery ON yourHugeQuery.DateTimeOfIssue BETWEEN aDate and DATEADD(DAY, 1, aDate)
GROUP BY aDate
ORDER BY aDate

mysql day report query

The query below gives me a report of items that are out for an equipment rental company. this is a super complicated query that takes almost 20 seconds to run. This is obviously not the correct way to get the data that I'm looking for. I build this query from PHP and add in the start date of 02-01-2011 and the end date of 03-01-2011, the product code (p_code = 1) and product pool (i_pool =1). Those 4 pieces of information are passed to a PHP function and injected into the following sql to return the report I need for a calendar control displaying how many items are out. My question is: Is there any way to simplify or do this better, or run more efficiently, using better joins or a better way to display the individual days.
SELECT DISTINCT reportdays.reportday, count(*)
FROM
(SELECT '2011-02-01' + INTERVAL a + b DAY reportday
FROM
(SELECT 0 a UNION SELECT 1 a UNION SELECT 2 UNION SELECT 3
UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7
UNION SELECT 8 UNION SELECT 9 ) d,
(SELECT 0 b UNION SELECT 10 UNION SELECT 20
UNION SELECT 30 UNION SELECT 40) m
WHERE '2011-02-01' + INTERVAL a + b DAY < '2011-03-01'
ORDER BY a + b) as reportdays
JOIN rental_inv as line
ON DATE(FROM_UNIXTIME(line.ri_delivery_dte)) <= reportdays.reportday
AND DATE(FROM_UNIXTIME(line.ri_pickup_dte)) >= reportdays.reportday
LEFT OUTER JOIN rental_in as rent on line.ri_num = rent.ri_num
LEFT OUTER JOIN rental_cancels cancelled on rent.ri_num = cancelled.ri_num
LEFT OUTER JOIN inv inventory on line.i_num = inventory.i_num
LEFT OUTER JOIN product ON inventory.p_code = product.p_code
WHERE rent.ri_extend = 0 -- disregard extended rentals
AND cancelled.ri_num is null -- disregard cancelled rentals
AND inventory.p_code = 1
AND inventory.i_pool = 1
GROUP BY reportdays.reportday
If there is any other information needed, let me know and I'll post it.
You can use:
SELECT DATE(ri_delivery) as day,
count(*) as itemsout,
FROM rental_inv
GROUP BY day;
I'm not sure if you need this or a different thing.
SELECT dates.day, count (*)
FROM rental_inv line
INNER JOIN (SELECT DATE(ri_delivery_dte) as day FROM rental_inv
WHERE ri_delivery_dte >= '2011/02/01'
AND ri_delivery_dte <= '2011/02/28'
GROUP BY day
UNION
SELECT DATE(ri_pickup_dte) as day FROM rental_inv
WHERE ri_pickup_dte >= '2011/02/01'
AND ri_pickup_dte <= '2011/02/28'
GROUP BY day) dates
ON line.ri_delivery_dte <= dates.day and line.ri_pickup_dte >= dates.day
LEFT JOIN rental_cancels canc on line.ri_num = canc.ri_num
LEFT JOIN rental_in rent on line.ri_num = rent.ri_num
WHERE canc.ri_num is null
AND rent.ri_extend = 0
GROUP BY dates.day
to find all days:
SELECT DATE(IFNULL(ri_delivery,ri_pickup)) AS date FROM rental_inv AS dateindex WHERE [YEAR-MONTH-1] <= ri_delivery <= LAST_DAY([YEAR-MONTH-1]) OR [YEAR-MONTH-1] <= ri_pickup <= LAST_DAY([YEAR-MONTH-1]) GROUP BY date HAVING NOT ISNULL(date)
to find items out
SELECT COUNT(id) FROM rental_inv WHERE ri_pickup = [DATE];
to find items in
SELECT COUNT(id) FROM rental_inv WHERE ri_delivery = [DATE];
to find balance
SELECT COUNT(out.id) - COUNT(in.id) FROM rental_inv AS out INNER JOIN rental_inv AS in
ON DATE(out.ri_pickup) = DATE(in.ri_delivery) WHERE out.ri_pickup = [DATE] OR in.ri_delivery = [DATE]
You probably can join up everything but since its procedure its more clear;
I am not sure if this would be the exact answer to your problem but I would do something like this I guess. (I didn't use any SQL editor so u need to check syntax I guess)
SELECT
reportdays.d3 as d,
( COALESCE(outgoing.c1,0) - COALESCE(incoming.c2,0) ) as c
FROM
-- get report dates
(
SELECT DATE(FROM_UNIXTIME(COALESCE(l3.ri_delivery_dte, l3.ri_pickup_dte)) d3
FROM rental_inv l3
WHERE
(l3.ri_delivery_dte >= UNIX_TIMESTAMP('2011-02-01')
AND l3.ri_delivery_dte < UNIX_TIMESTAMP('2011-03-01'))
OR (l3.ri_pickup_dte >= UNIX_TIMESTAMP('2011-02-01')
AND l3.ri_pickup_dte < UNIX_TIMESTAMP('2011-03-01'))
GROUP BY d3
) as reportdays
-- get outgoing
LEFT JOIN (
SELECT DATE(FROM_UNIXTIME(l1.ri_delivery_dte)) as d1, count(*) as c1
FROM rental_inv l1
LEFT JOIN rental_cancels canc1 on l.ri_num = canc1.ri_num
LEFT JOIN rental_in rent1 on l.ri_num = rent1.ri_num
WHERE
l1.ri_delivery_dte >= UNIX_TIMESTAMP('2011-02-01')
AND l1.ri_delivery_dte < UNIX_TIMESTAMP('2011-03-01')
AND canc1.ri_num is null
AND rent1.ri_extend = 0
GROUP BY d1
) as outgoing ON reportdays.d3 = outgoing.d1
-- get incoming
LEFT JOIN (
SELECT DATE(FROM_UNIXTIME(l2.ri_pickup_dte)) as d2, count(*) as c2
FROM rental_inv l2
LEFT JOIN rental_cancels canc2 on l2.ri_num = canc2.ri_num
LEFT JOIN rental_in rent2 on l2.ri_num = rent2.ri_num
WHERE
l2.ri_pickup_dte >= UNIX_TIMESTAMP('2011-02-01')
AND l2.ri_pickup_dte < UNIX_TIMESTAMP('2011-03-01')
AND canc2.ri_num is null
AND rent2.ri_extend = 0
GROUP BY d2
) as incoming ON reportdays.d3 = incoming.d2