Show Zero Sales For Item in SQL Query - sql

Being new to SQL I have a problem with my query that I am sure has a trivial solution. I am trying to make a report for item sales in a specific period and I need to display 0 or empty cell if there are no sales. Instead I don't get item displayed at all.
Current query is:
SELECT stock.stock_item_code, stock.physical_quantity, sales.quantity
FROM stock_branch_level AS stock LEFT OUTER JOIN
booked_sales AS sales ON stock.stock_item_code = sales.stock_item_code
WHERE (stock.replen_type = 1) AND (sales.created_date > #DateFrom)
Thanks

LEFT OUTER JOIN is the right way to get rows even if there are no sales.
The problem is with your where clause on sales.
Note that when there are no sales all sales columns will be null in that row!
The solution is to move the restriction on sales.created_date to the join clause:
SELECT stock.stock_item_code, stock.physical_quantity, sales.quantity
FROM stock_branch_level AS stock LEFT OUTER JOIN
booked_sales AS sales ON stock.stock_item_code = sales.stock_item_code
AND sales.created_date > #DateFrom
WHERE (stock.replen_type = 1)

Related

How To Display Customers Even If They Have No Sales (results)

I have a code that pulls all of a Reps new customers and their year to date sales. The only problem is it only pulls customers that have sales in the invdate range, but I need it to show all of the accounts with a 0 if they do not have any sales. Is there any way to achieve this? I tried using COALESCE and it didn't seem to work. I also tried using left, right, full outer joins. Any help would be appreciated!
select
a.Acctnum,
sum(a.invtotal) as total
from invoices a right join accounts b on a.acctnum = b.acctnum where
a.invdate between '1/1/2017' and '12/31/2017'
and a.sls = '78'
and b.sls = '78'
and b.activetype = 'y' and b.startdate > (getdate()-365)
group by a.acctnum
order by total desc
You are restricting your results in your WHERE clause AFTER you join your table causing records to drop. Instead, switch to a LEFT OUTER JOIN with your accounts table driving the query. Then restrict your invoices table in your ON clause so that invoices are dropped BEFORE you join.
SELECT a.Acctnum,
sum(a.invtotal) AS total
FROM accounts b
LEFT OUTER JOIN invoices a ON
a.accntnum = b.acctnum AND
--Put the restrictions on your left most table here
--so they are removed BEFORE joining.
a.invdate BETWEEN '1/1/2017' AND '12/31/2017'
AND a.sls = '78'
WHERE
b.sls = '78'
AND b.activetype = 'y'
AND b.startdate > (getdate() - 365)
GROUP BY a.acctnum
ORDER BY total DESC
It's a bit like doing a subquery in invoices before joining in as the left table. It's just easier to drop the conditions into the ON clause.
Your problem is you where clauses are changing the right join to an inner join. Put all the ones that are aliased by a. into the ON clause.

Joining 2 tables error

I'm trying to join 2 tables to get an output report. The tables involved are the stock and dailysales table.
Stock and Dailysales tables:
Desired output format:
I am trying to join 2 tables by using the below query
Select item,article,sold,stockonhand
from stock S
left join dailysales as D on S.item=D.item
group by item
I want the output to include all rows from the stock table. Like there is stock, but not sold, also to be included in the report. Currently my report is does not show, (stocked but not sold).
Hope you can understand my context or point me to a tutorial which I could read up. I tried to search for few days and couldn't find an exact question to mine.
Not tested -
select item,article,sum(sold),sum(stockonhand)-sum(sold) from (
select a.item,a.article,a.stockonhand,case when b.sold is null then 0 else b.sold end as sold
from stock a left join dailysales b on (a.item = b.item))
group by item,article;
It's basically does the left join and put 0 on the null column(for sum after)
and then summing up the results grouping by all the columns in the select(that what was wrong with your query)
Simply LEFT JOIN the two tables (to get the flute too), do a GROUP BY, and SUM the sold:
select s.item, s.article, coalesce(SUM(d.sold),0) as "qty sold", s.stockonhand
from stock S
left join dailysales as D on S.item=D.item
group by s.item, s.article, s.stockonhand
The coalesce is there to replace NULL with 0, for items not sold. (Thanks sagi!)
General GROUP BY tip: If a GROUP BY clause is specified, each column reference in the SELECT list must either identify a grouping column or be the argument of a set function.
Also, you can remove the article column from the dailysales table. That data is already stored in the stock table. Never store same data twice! (Normalization!) Risk for data inconsistency if you keep that column.
You can sum the item in a separated table, it would be clearer than GROUP BY 2 tables
SELECT
s.item,
s.article,
ISNULL(ds.sold, 0) AS qtysold,
s.stockonhand
FROM
stock s OUTER APPLY
(SELECT SUM(sold) AS sold FROM dailysales WHERE item = s.item GROUP BY item) ds

Using a where statement against left outer join

I have a query that returns ALL the appointments for an advisor in a week, and what items they sold at that appointment (if non have been sold it still shows the appointment) I have done this by using a left outer join on the appointments table and ithe items table.
In this scenario I only want to display sold items in stock ( in stock is a field in the items table)
If I use a 'where instock true' against items, I loose all the appointments where no items have been sold ?
Do I need to nest these queries somehow - if so how ? Thanks
The key to what you want to do is to move the condition on the stock to the on clause from the where clause. Presumably, your existing query looks something like this:
select <whatever>
from appointments a left join
sells s
on a.appointmentid = s.appointmentid
where s.instock = 1;
The where clause is turning the left join into an inner join. When there is no match, the value of instock is NULL, which does not match 1. The solution is to move the condition to the on clause:
select <whatever>
from appointments a left join
sells s
on a.appointmentid = s.appointmentid and
s.instock = 1;
You should use ON instead WHERE. That way, if the items aren't in stock the wont match and the right part of your join will be NULL.
SELECT A.*, S.* FROM appointments A LEFT JOIN sells S ON ... AND sells.instock = 1;
I could be more precise if I had the SQL you've tried to write.

Show dates older than current date in sql query

In my sql statement I have an SQL statement which i need to show which customers return dates are overdue i.e. older than current date, so far my query looks like the following:
SELECT customers.firstname, items.itemname, CustEquipment.ReturnDate, customers.phonenumber
FROM items join
CustEquipment
on items.ItemId = CustEquipment.ItemId join
customers
on CustEquipment.customerID=customers.customerID
This currently shows all customers and their return dates (along with other info i need) but i want to show the return dates which have gone past the current date, anyone care to point me in the right direction please?
You can do the date comparison in the where clause:
SELECT customers.firstname, items.itemname, CustEquipment.ReturnDate, customers.phonenumber
FROM items join
CustEquipment
on items.ItemId = CustEquipment.ItemId join
customers
on CustEquipment.customerID=customers.customerID
where ReturnDate <= trunc(sysdate);
I also adjusted your query to use correct join syntax.
select C.Firstname,I.ItemName,CE.ReturnDate,C.PhoneNumber
from Items I
inner join CustEquipments CE
on
CE.ItemID=I.ItemID
inner join Customers C
on
C.CustomerID=CE.CustomerID
where
convert(varchar(10),getdate(),101)>convert(varchar(10),CE.ReturnDate,101)

Left and right joining in a query

A friend asked me for help on building a query that would show how many pieces of each model were sold on each day of the month, showing zeros when no pieces were sold for a particular model on a particular day, even if no items of any model are sold on that day. I came up with the query below, but it isn't working as expected. I'm only getting records for the models that have been sold, and I don't know why.
select days_of_months.`Date`,
m.NAME as "Model",
count(t.ID) as "Count"
from MODEL m
left join APPLIANCE_UNIT a on (m.ID = a.MODEL_FK and a.NUMBER_OF_UNITS > 0)
left join NEW_TICKET t on (a.NEW_TICKET_FK = t.ID and t.TYPE = 'SALES'
and t.SALES_ORDER_FK is not null)
right join (select date(concat(2009,'-',temp_months.id,'-',temp_days.id)) as "Date"
from temp_months
inner join temp_days on temp_days.id <= temp_months.last_day
where temp_months.id = 3 -- March
) days_of_months on date(t.CREATION_DATE_TIME) =
date(days_of_months.`Date`)
group by days_of_months.`Date`,
m.ID, m.NAME
I had created the temporary tables temp_months and temp_days in order to get all the days for any month. I am using MySQL 5.1, but I am trying to make the query ANSI-compliant.
You should CROSS JOIN your dates and models so that you have exactly one record for each day-model pair no matter what, and then LEFT JOIN other tables:
SELECT date, name, COUNT(t.id)
FROM (
SELECT ...
) AS days_of_months
CROSS JOIN
model m
LEFT JOIN
APPLIANCE_UNIT a
ON a.MODEL_FK = m.id
AND a.NUMBER_OF_UNITS > 0
LEFT JOIN
NEW_TICKET t
ON t.id = a.NEW_TICKET_FK
AND t.TYPE = 'SALES'
AND t.SALES_ORDER_FK IS NOT NULL
AND t.CREATION_DATE_TIME >= days_of_months.`Date`
AND t.CREATION_DATE_TIME < days_of_months.`Date` + INTERVAL 1 DAY
GROUP BY
date, name
The way you do it now you get NULL's in model_id for the days you have no sales, and they are grouped together.
Note the JOIN condition:
AND t.CREATION_DATE_TIME >= days_of_months.`Date`
AND t.CREATION_DATE_TIME < days_of_months.`Date` + INTERVAL 1 DAY
instead of
DATE(t.CREATION_DATE_TIME) = DATE(days_of_months.`Date`)
This will help make your query sargable (optimized by indexes)
You need to use outer joins, as they do not require each record in the two joined tables to have a matching record.
http://dev.mysql.com/doc/refman/5.1/en/join.html
You're looking for an OUTER join. A left outer join creates a result set with a record from the left side of the join even if the right side does not have a record to be joined with. A right outer join does the same on the opposite direction, creates a record for the right side table even if the left side does not have a corresponding record. Any column projected from the table that does not have a record will have a NULL value in the join result.