I have a seemingly simple problem that I can't seem to solve. Imagine an order table with a shipping value for that order. And imagine an item table that point to the order, and have a price:
Order
|id |created |shipping|
|---|----------|--------|
|101|2018-01-01|10 |
|102|2018-01-01|10 |
Item
|order_id|price |
|--------|--------|
|101 |1 |
|101 |2 |
|102 |3 |
So there were two orders. Each had a shipping fee of $10. One order had two items, priced $1 and $2. The other had one item priced $3. Very simple so far.
I want to write a query, that will find the total shipping price and the total item price (summed) for every day (group by created). Naively, I would write this query:
SELECT
o.created, sum(o.shipping), sum(i.price)
FROM
test_order o,
test_item i
WHERE
i.order_id = o.id
group by o.created
This query is very simple. It joins the two tables, and then group by that date, and sums vertically. The problem is with the shipping part. Since there are three rows after the join, there are three $10 shipping fees in the sum, which is entirely incorrect.
I could add a distinct to my shipping clause (sum(distinct o.shipping)), but that, too, is incorrect. It would evaluate those three 10s, and only keep one, resulting in a summed value of $10. It doesn't recognize that two of those 10s came from one source row, while the third 10 comes from an entirely different source row, and is desirable to include it in the sum.
My real query is much more complicated that this, but I distilled it down to this. I would think this is a common situation for many use cases. Of course, I could write a second query (or a sub query), but I'd like to not do that if at all possible.
Wanna play with this data yourself? Here is the sql:
CREATE TABLE IF NOT EXISTS `test_item` (
`order_id` int(11) NOT NULL,
`price` int(11) NOT NULL)
INSERT INTO `test_item` (`order_id`, `price`) VALUES (101, 1), (101, 2), (102, 3);
CREATE TABLE IF NOT EXISTS `test_order` (
`id` int(12) NOT NULL,
`created` datetime NOT NULL,
`shipping` int(11) NOT NULL,
PRIMARY KEY (`id`)
)
INSERT INTO `test_order` (`id`, `created`, `shipping`) VALUES (101, '2018-01-01 00:00:00', 10), (102, '2018-01-01 00:00:00', 10);
Thanks!
You are trying to avoid subqueries, but in this case I can't see other options:
SELECT created, sum(shipping), sum(price) from (
SELECT
o.created, o.shipping, sum(i.price) as price
FROM
test_order o,
test_item i
WHERE
i.order_id = o.id
group by o.created, o.id) subquery;
Here the fiddle.
Using intermediate alias table would do the trick :
select sumTable.createdDate, sum(sumTable.shippingSum),sum(sumTable.priceSum) from (
SELECT
o.created, (o.shipping) as shippingSum, sum(i.price) as priceSum
FROM
test_order o,
test_item i
WHERE
i.order_id = o.id
group by o.created,o.id)
as sumTable
I tested in SQL Fiddle over here resulting in sum of $30 and $6, which is probably what you wanted.
Related
I'm new to views in MySQL server.
I'm trying to create two VIEWS with "invoice_head" and "invoice_body", but in order to do that i need to get different keys from different tables (this tables have diferent number of columns).
This is the code that i'm trying to use:
CREATE VIEW invoice_head AS
SELECT
date_order AS 'DATE OF ISSUE'
FROM orders
UNION ALL
SELECT
customer_name AS 'Client Name',
customer_address AS 'Street adress',
customer_district AS 'District',
customer_country AS 'Portugal',
customer_zipcode AS 'ZIP Code'
FROM customer;
but this code is giving me the error: The used SELECT statements have different number of columns.
And the tables i'm using are something like this (for example)
Customer Table (8 columns)
customer_id
customer_name
customer_address
customer_district
(...)
1
maria xxx
street xxx
lisbon
(...)
Orders Table (9 columns)
order_id
customer_id
product_id
date_order
(...)
10
1
20
2020-12-15
(...)
The invoice_head is supposed to have information about the customer, the invoice_id, and the date of the order (invoice_date aka date_order).
Can someone tell me in what way is this possible to be made?
Is it impossible to do or I need to do something I'm not seeing?
Someone in the comments helped me. I just needed to use the "Join" instead of "Select":
DROP VIEW IF EXISTS `invoice_head`;
CREATE VIEW `invoice_head` AS
SELECT date_order as `Date of issue`,
customer_name AS `Client Name`,
customer_address AS `Street adress`,
customer_district AS `District`,
customer_country AS `Country`,
customer_zipcode AS `ZIP Code`
FROM orders
join customer
on orders.customer_id = customer.customer_id;
I'm trying to make a query for get some trend stats, but the benchmark is really slow. The query execution time is around 134 seconds.
I have a MySQL table called table_1.
Below the create statement
CREATE TABLE `table_1` (
`id` bigint(11) NOT NULL AUTO_INCREMENT,
`original_id` bigint(11) DEFAULT NULL,
`invoice_num` bigint(11) DEFAULT NULL,
`registration` timestamp NULL DEFAULT NULL,
`paid_amount` decimal(10,6) DEFAULT NULL,
`cost_amount` decimal(10,6) DEFAULT NULL,
`profit_amount` decimal(10,6) DEFAULT NULL,
`net_amount` decimal(10,6) DEFAULT NULL,
`customer_id` bigint(11) DEFAULT NULL,
`recipient_id` text,
`cashier_name` text,
`sales_type` text,
`sales_status` text,
`sales_location` text,
`invoice_duration` text,
`store_id` double DEFAULT NULL,
`is_cash` int(11) DEFAULT NULL,
`is_card` int(11) DEFAULT NULL,
`brandid` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_registration_compound` (`id`,`registration`)
) ENGINE=InnoDB AUTO_INCREMENT=47420958 DEFAULT CHARSET=latin1;
I have set a compound index made of id+registration.
Below the query
SELECT
store_id,
CONCAT('[',GROUP_CONCAT(tot SEPARATOR ','),']') timeline_transactions,
SUM(tot) AS total_transactions,
CONCAT('[',GROUP_CONCAT(totalRevenues SEPARATOR ','),']') timeline_revenues,
SUM(totalRevenues) AS revenues,
CONCAT('[',GROUP_CONCAT(totalProfit SEPARATOR ','),']') timeline_profit,
SUM(totalProfit) AS profit,
CONCAT('[',GROUP_CONCAT(totalCost SEPARATOR ','),']') timeline_costs,
SUM(totalCost) AS costs
FROM (select t1.md,
COALESCE(SUM(t1.amount+t2.revenues), 0) AS totalRevenues,
COALESCE(SUM(t1.amount+t2.profit), 0) AS totalProfit,
COALESCE(SUM(t1.amount+t2.costs), 0) AS totalCost,
COALESCE(SUM(t1.amount+t2.tot), 0) AS tot,
t1.store_id
from
(
SELECT a.store_id,b.md,b.amount from ( SELECT DISTINCT store_id FROM table_1) AS a
CROSS JOIN
(
SELECT
DATE_FORMAT(a.DATE, "%m") as md,
'0' as amount
from (
select curdate() - INTERVAL (a.a + (10 * b.a) + (100 * c.a)) month as Date
from (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as a
cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as b
cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as c
) a
where a.Date >='2019-01-01' and a.Date <= '2019-01-14'
group by md) AS b
)t1
left join
(
SELECT
COUNT(epl.invoice_num) AS tot,
SUM(paid_amount) AS revenues,
SUM(profit_amount) AS profit,
SUM(cost_amount) AS costs,
store_id,
date_format(epl.registration, '%m') md
FROM table_1 epl
GROUP BY store_id, date_format(epl.registration, '%m')
)t2
ON t2.md=t1.md AND t2.store_id=t1.store_id
group BY t1.md, t1.store_id) AS t3 GROUP BY store_id ORDER BY total_transactions desc
Below the EXPLAIN
Maybe I should change from timestamp to datetime in registration column?
About 90% of your execution time will be used to execute GROUP BY store_id, date_format(epl.registration, '%m').
Unfortunately, you cannot use an index to group by a derived value, and since this is vital to your report, you need to precalculate this. You can do this by adding that value to your table, e.g. using a generated column:
alter table table_1 add md varchar(2) as (date_format(registration, '%m')) stored
I kept the varchar format you used for the month here, you could also use a number (e.g. tinyint) for the month.
This requires MySQL 5.7, otherwise you can use triggers to achieve the same thing:
alter table table_1 add md varchar(2) null;
create trigger tri_table_1 before insert on table_1
for each row set new.md = date_format(new.registration,'%m');
create trigger tru_table_1 before update on table_1
for each row set new.md = date_format(new.registration,'%m');
Then add an index, preferably a covering index, starting with store_id and md, e.g.
create index idx_table_1_storeid_md on table_1
(store_id, md, invoice_num, paid_amount, profit_amount, cost_amount)
If you have other, similar reports, you may want to check if they use additional columns and could profit from covering more columns. The index will require about 1.5GB of storage space (and how long it takes your drive to read 1.5GB will basically single-handedly define your execution time, short of caching).
Then change your query to group by this new indexed column, e.g.
...
SUM(cost_amount) AS costs,
store_id,
md -- instead of date_format(epl.registration, '%m') md
FROM table_1 epl
GROUP BY store_id, md -- instead of date_format(epl.registration, '%m')
)t2 ...
This index will also take care of another other 9% of your execution time, SELECT DISTINCT store_id FROM table_1, which will profit from an index starting with store_id.
Now that 99% of your query is taken care of, some further remarks:
the subquery b and your date range where a.Date >='2019-01-01' and a.Date <= '2019-01-14' might not do what you think it does. You should run the part SELECT DATE_FORMAT(a.DATE, "%m") as md, ... group by md separately to see what it does. In its current state, it will give you one row with the tuple '01', 0, representing "january", so it is basically a complicated way of doing select '01', 0. Unless today is the 15th or later, then it returns nothing (which is probably unintended).
Particularly, it will not limit the invoice dates to that specific range, but to all invoices that are from (the whole) january of any year. If that is what you intended, you should (additionally) add that filter directly, e.g. by using FROM table_1 epl where epl.md = '01' GROUP BY ..., reducing your execution time by an additional factor of about 12. So (apart from the 15th and up-problem), with your current range you should get the same result if you use
...
SUM(cost_amount) AS costs,
store_id,
md
FROM table_1 epl
WHERE md = '01'
GROUP BY store_id, md
)t2 ...
For different date ranges you will have to adjust that term. And to emphasize my point, this is significantly different from filtering invoices by their date, e.g.
...
SUM(cost_amount) AS costs,
store_id,
md
FROM table_1 epl
WHERE epl.registration >='2019-01-01'
and epl.registration <= '2019-01-14'
GROUP BY store_id, md
)t2 ...
which you may (or may not) have tried to do. You would need a different index in that case though (and it would be a slightly different question).
there might be some additional optimizations, simplifications or beautifications in the rest of your query, e.g group BY t1.md, t1.store_id looks redundant and/or wrong (indicating you are actually not on MySQL 5.7), and the b-subquery can only give you values 1 to 12, so generating 1000 dates and reducing them again could be simplified. But since they are operating on 100-ish rows, they will not affect execution time significantly, and I haven't checked those in detail. Some of it is probably due to getting the right output format or to generalizations (although, if you are dynamically grouping by other formats than by month, you need other indexes/columns, but that would be a different question).
An alternative way to precalculate your values would be a summary table where you e.g. run your inner query (the expensive group by) once a day and store the result in a table and then reuse it (by selecting from this table instead of doing the group by). This is especially viable for data like invoices that never change (although otherwise you can use triggers to keep the summary tables up to date). It also becomes more viable if you have several scenarios, e.g. if your user can decide to group by weekday, year, month or zodiac sign, since otherwise you would need to add an index for each of those. It becomes less viable if you need to dynamically limit your invoice range (to e.g. 2019-01-01 ... 2019-01-14). If you need to include the current day in your report, you can still precalculate and then add the values for the current date from the table (which should only involve a very limited number of rows, which is fast if you have an index starting with your date column), or use triggers to update your summary table on-the-fly.
With PRIMARY KEY(id), having INDEX(id, anything) is virtually useless.
See if you can avoid nesting subqueries.
Consider building that 'date' table permanently and have a PRIMARY KEY(md) on it. Currently, neither subquery has an index on the join column (md).
You may have the "explode-implode" syndrome. This is where JOINs expand the number of rows, only to have the GROUP BY collapse them.
Don't use COUNT(xx) unless you need to check xx for being NULL. Simply do COUNT(*).
store_id double -- Really?
TIMESTAMP vs DATETIME -- they perform about the same; don't bother changing it.
Since you are only looking at 2019-01, get rid of
date_format(epl.registration, '%m')
That, alone, may speed it up a lot. (However, you lose generality.)
I'm doing a basic SQL select query which returns a set of results. I want a specific row which the entry "Fee" to be put at the top of the results, then the rest.
Something like:
SELECT * FROM tbl ORDER By Charges = Fee DESC, Charges DESC
Can anyone help?
You could try this :
SELECT * from tbl ORDER BY CASE WHEN Charges = 'Fee' THEN 0 ELSE 1 END, Charges DESC;
I think you'd have a use a UNION query. ORDER BY doesn't support this kind of thing by default as far as I know.
Something like this:
SELECT * FROM tbl WHERE Charges = 'Fee'
UNION
SELECT * FROM tbl ORDER BY Charges DESC
You would have to use ORDER BY with a FIELD attribute, which would then order by those first.
As I don't have your table definitions, I have throw one together here http://sqlfiddle.com/#!9/91376/13
For sake of it disappearing, the script pretty much consists of;
CREATE TABLE IF NOT EXISTS `tbl` (
`id` int(6) unsigned AUTO_INCREMENT,
`Name` char(6) not null,
`Charges` char(10) NOT NULL,
PRIMARY KEY (`id`)
) DEFAULT CHARSET=utf8;
INSERT INTO `tbl` (`Name`, `Charges`)
VALUES ('One', 'Fee'), ('Two', 'Charge'), ('Three', 'Charge'),
('Four', 'Charge'), ('Five', 'Fee'), ('Six', 'Fee'),
('Seven', 'Invoice'), ('Eight', 'Fee'), ('Nine', 'Invoice'),
('Ten', 'Invoice');
SELECT *
FROM tbl
ORDER BY FIELD(`Charges`, 'Charge') DESC
;
Which returns:
id Name Charges
2 Two Charge
3 Three Charge
4 Four Charge
1 One Fee
9 Nine Invoice
8 Eight Fee
7 Seven Invoice
6 Six Fee
5 Five Fee
10 Ten Invoice
So, to directly answer your question, your query would be;
SELECT *
FROM tbl
ORDER BY FIELD(Charges, 'Fee') DESC
edit : Viewable, sorted by Charges = Fee here : http://sqlfiddle.com/#!9/91376/15
SELECT * FROM tbl ORDER By FIELD(Charges, 'Fee') DESC
You can use something like the above. Where Charges is the field and fee the specific value. That way you can keep it simple.
Wasn't sure how to ask this question. Basically I have a table:
create table sales (
bookcode int not null,
storecode int not null,
saledate date not null,
quantity int not null,
price decimal,
foreign key (bookcode) references books(bookcode),
foreign key (storecode) references stores(storecode)
);
and what I'm trying to do here is calculate the revenue for each store code. It will multiply the quantity by the price which is fine but I don't know how to get it so if there are multiple of the same storecode, it groups them together and gives a total amount
For example: if there are two stores in the sales table with the same store code of 1, I want to get the revenue of both and have them show up as a mass total for that storecode, instead of showing them separately. How can I do this?
What you are looking for is a GROUP BY clause, e.g.
SELECT storecode, SUM(quantity * price) AS total_sales
FROM sales
GROUP BY storecode;
This clause directs the SQL engine to group together rows which have an identical storecode, then apply aggregate functions to combine the results from each set of rows. In this case, the aggregate function used is SUM(); other common aggregate functions include COUNT(), AVG(), MIN(), MAX(), and GROUP_CONCAT().
Keep in mind that, if you're using a GROUP BY clause, every column you select must be either one of the values that's being GROUPed BY, or an aggregate function. For instance, you could not:
SELECT storecode, saledate FROM sales GROUP BY storecode /* no! */
because saledate is neither a column that's being grouped by, nor an aggregate function. (To put it simply: there's multiple sale dates, which one is the query engine supposed to give you?)
I think you need something like this:
SELECT storecode, SUM(price*quantity) as revenue FROM sales GROUP BY storecode;
CREATE TABLE IF NOT EXISTS sales (
bookcode int(6) unsigned NOT NULL,
storecode int(6) unsigned NOT NULL,
saledate date NOT NULL,
quantity int(6) unsigned NOT NULL,
price decimal
) DEFAULT CHARSET=utf8;
INSERT INTO sales (bookcode, storecode, saledate, quantity, price) VALUES
('1', '1', '2017-09-24',3,5),
('1', '1', '2017-09-24',5,5),
('1', '2', '2017-09-24',10,5),
('1', '2', '2017-09-24',10,10);
select storecode,sum(quantity*price)as income from sales group by storecode
I am attempting to write an SQL query for a reservations system to find the earliest available rental date for an item.
Items are categorized in the database by SKU. There can be multiple copies of an item, each uniquely identified in the database by its serial number.
When searching for the earliest available rental date for an item, it doesn't matter which serial number is chosen; simply the next one available.
The database has 2 tables; "Reservations" and "Items". There is also a Calendar table of several thousand YYYY-MM-DD future dates to work with.
The Reservations table contains columns; "record_number","sku", "serial_number", "start_date", "end_date" plus customer data. This is where each reservation is recorded as it is made.
The Items table contains columns; "sku" and "serial_number". This is an inventory of all rental items in the system.
I've worked the problem for over 2 days, but my SQL knowledge isn't enough to solve this puzzle.
I've progressed as far as generating a list of dates that have at least one reservation for a particular SKU:
SELECT calendar.dt
FROM calendar
LEFT JOIN reservations ON calendar.dt >= reservations.start_date
AND calendar.dt <= reservations.end_date
WHERE reservations.sku = 'ABC123'
I can sub-query the above into a "NOT IN ..." select statement but that only accomplishes finding dates having NO reservations for a particular SKU. I need to find the first date where at least one item is available.
I have imagined joining the dates of the Calendar table with the SKUs from the Items table with the reservation numbers of the Reservation table looking for "NULL" in the reservation_record, indicating no reservation exists for that date and serial-number combination. But I have been unable to write such a query that works.
Questions are welcome.
The following should get you going. you may want to adjust my sample of "Current_Date()" function for whatever may be your reservation start date and going out so many days....
This uses MySQL inline variables in the query. The inner query is a simple prepare of a reservation (#r) variable based on some starting date ( current_date() ), and joins to the item table. By doing no join clause, it would otherwise grab one date for every item. In my scenario, I'm only considering going out 30 days, so I've applied a limit of the first 30 items. No basis other than give me enough records so I don't have to create a temp table of 30 records (or however many days you want to go out). This creates an aliased query "JustDates" and has a single column "OpenDate". This is the basis of date ranges to test for.
This is now joined to the items table, but no condition creates a Cartesian to say for each date, compare with every item... per the WHERE clause, I am only concerned with items having SKU of "ABC123" weather they have 10 serial #s or 100. This would now give me a possible 300 or 3000 (10 serial items # 30 days, or 100 serial items # 30 days.
Now that I have a "range" of all individual serial numbers and possible days to check availability, I can now query against the reservations system. So, via a sub-select, and NOT IN for a given matching SKU, SERIAL #, and the POSSIBLE Date being found in reservations, I only want to keep those where the given OpenDate is NOT found. I've simulated your table structures and put in a handful of items, multiple serial numbers and staggared reservation date ranges and it works great...
Obviously, I would ensure indexes on sku / serial for performance. The only additional change I might make is when querying against the reservations, to exclude any reservations where the end date is prior to the starting date in question for YOUR query, and optionally, no Start Date > the LAST date you are considering. If you have a ton of reservations spanning years, who cares about something ancient, or something way in the future from the date range in question.
select items.sku,
items.serial_number,
JustDates.OpenDate
from
( SELECT
#r:= date_add(#r, interval 1 day ) OpenDate
FROM
(select #r := current_date()) vars,
items limit 30 ) JustDates,
items
where
sku = "ABC123"
and sku not in ( select sku from Reservations
where items.sku = reservations.sku
and items.serial_number = reservations.serial_number
and justDates.OpenDate >= reservations.start_date
and justDates.OpenDate <= reservations.end_date )
order by
items.serial_number,
justDates.OpenDate
Unfortunately, you can't look for missing rows (you can for rows that don't exist, but it's not the same).
You had a good start, querying the calendar table and left joining to the reservations, but you made a mistake by putting the filtering condition in the WHERE clause where it will return no rows instead of in the join ON clause.
SELECT calendar.dt,
COUNT(DISTINCT reservations.id) AS reserved,
COUNT(DISTINCT items.id) AS inventory
FROM calendar
LEFT JOIN reservations
ON calendar.dt >= reservations.start_date
AND calendar.dt <= reservations.end_date
AND reservations.sku = 'ABC123'
LEFT JOIN items ON items.sku=reservations.sku
GROUP BY calendar.dt
HAVING inventory > reserved OR reserved = 0
ORDER BY calendar.dt ASC
LIMIT 1
This query looks for the earliest date in the calendar table where the existing number of items in the items table is more than the number of reservations for those dates (ie, first date where you have stock available).
(EDIT: Substitute 'id' with the primary column in those tables)
I learned a really valuable lesson from a SQL expert a long time ago.
Don't let what you have blind you to what you need. Think about where reservations are used in the real world.
Hotels
Airlines
Cruise ships
Arenas
Football stadiums
Every one of these has a fixed inventory of seats on a particular day. Each of those seats might be either open or reserved--tied to a specific customer.
You have a fixed inventory of items on a particular day. Each of those items might be either available or reserved--tied to a specific customer.
I think your life would be a lot easier if you created a table of availability rather than a table of reservations.
create table availability (
sku varchar(15) not null,
sn varchar(15) not null,
av_date date not null,
customer_id integer, --references customers (customer_id)
primary key (sku, sn, av_date),
foreign key (sku, sn) references items (sku, sn)
);
insert into availability values
('1', '1', '2011-01-01', 1), -- reserved by customer_id 1
('1', '1', '2011-01-02', 1),
('1', '1', '2011-01-03', 1),
('1', '1', '2011-01-04', NULL), -- not yet reserved
('1', '1', '2011-01-05', NULL),
('1', '1', '2011-01-06', NULL),
('1', '1', '2011-01-07', NULL),
('1', '1', '2011-01-08', NULL),
('1', '1', '2011-01-09', NULL),
('1', '1', '2011-01-10', NULL),
('1', '2', '2011-01-01', NULL),
('1', '2', '2011-01-02', 2),
('1', '2', '2011-01-03', 2),
('1', '2', '2011-01-04', 3),
('1', '2', '2011-01-05', 3),
('1', '2', '2011-01-06', NULL),
('1', '2', '2011-01-07', NULL),
('1', '2', '2011-01-08', NULL),
('1', '2', '2011-01-09', NULL),
('1', '2', '2011-01-10', NULL);
(For production, I'd build a stored procedure to populate the availability table.)
Now it's just dead simple to select the earliest available date for a given sku.
select sku, sn, av_date, customer_id
from availability
where sku = '1' and customer_id is null
order by av_date asc
limit 1
The request start and end dates are the periods of time over which someone wants to reserve the given item. (E.g., "I want to reserve the widget sometime between July and September")
Revised to handle existing stock on-hand. Although, it isn't clear how that is actually stored. Do you have an on-hand value for each SKU or is each item given its own row?
Select Min( C.dt ) As EarliestAvailableDate
From Calendar As C
Left Join Reservations As R
On R.StartDate <= C.dt
And R.EndDate >= C.dt
And R.Sku = 'ABC123'
Where C.dt Between '[Request Start Date]' And '[Request End Date]'
And R.record_number Is Null
And Exists (
Select 1
From Items As I1
Left Join Reservations As R1
On R1.StartDate <= C.dt
And R1.EndDate >= C.dt
And R1.Sku = I1.SKU
Where I.SKU = 'ABC123'
Having Count(*) > Count(R1.record_number)
)
If your items table actually stores an on-hand value for the number of a given SKU in stock, then yo can simply change the query like so:
Select Min( C.dt ) As EarliestAvailableDate
From Calendar As C
Left Join Reservations As R
On R.StartDate <= C.dt
And R.EndDate >= C.dt
And R.Sku = 'ABC123'
Where C.dt Between '[Request Start Date]' And '[Request End Date]'
And R.record_number Is Null
And Exists (
Select 1
From Items As I1
Left Join Reservations As R1
On R1.StartDate <= C.dt
And R1.EndDate >= C.dt
And R1.Sku = I1.SKU
Where I.SKU = 'ABC123'
Having I1.OnHandCount > Count(R1.record_number)
)