MySQL - conditional SELECT - sql

I have tables item and store (it's a store management system). item table has a column called store_id and another column called status. item.status can be 'sold' or 'unsold'.
I need help writing a query which will do these things:
select all items of all stores
if a store has just one item and that item is 'sold', remove that item from the result set
Thanks in advance!

You could create a filtering subquery that searches for stores with more than one item, or one unsold item. Then you can join the subquery on the original tables, like:
select *
from (
select s2.store_id
from store s2
join items i2
on s2.store_id = i2.store_id
group by
s2.store_id
having
count(*) > 1 -- More than one item
or max(i2.status) = 'unsold' -- One item but unsold
) filter
join store s
on filter.store_id = s.store_id
join items i
on s.store_id = i.store_id

Related

SQL subqueries return records, but the main query as a whole returns zero rows

This query fetches zero rows as a whole. I've tried running each of the sub-queries separately, and each works fine.
The main table ITEMS contains all items in the inventory. The table ISSUED contains item IDs which have been issued out from the main store.
I'm running two sub-queries in this main query. One which fetches the count of all items in the inventory which were entered into the stock, on a given day and haven't been issued, and the other which fetches the count of all items which were entered into the inventory on that day, but were issued out on the same day.
I need to get the sum of both these counts of items grouped by item name.
SELECT ITEMS.ITEM AS ITEM,
( IFNULL(Arr1.TAG_COUNT, 0)
+ IFNULL(Arr2.TAG_COUNT, 0) ) AS TAG_COUNT
FROM ((SELECT ITEMS.ITEM AS ITEM,
Count(ITEMS.ID) AS TAG_COUNT
FROM ITEMS
WHERE ITEMS.DT_ARRIVAL = '2019-01-01'
AND ITEMS.ID NOT IN (SELECT ISSUED.ID
FROM ISSUED)
GROUP BY ITEMS.ITEM) AS Arr1
LEFT JOIN (SELECT ITEMS.ITEM AS ITEM,
Count(ITEMS.ID) AS TAG_COUNT
FROM ITEMS
LEFT JOIN ISSUED
ON ISSUED.ID = ITEMS.ID
WHERE ITEMS.ID IN (SELECT ISSUED.ID
FROM ISSUED)
AND ISSUED.DATE = '2019-01-01'
GROUP BY ITEMS.ITEM) AS Arr2
ON Arr2.ITEM = Arr1.ITEM
LEFT JOIN ITEMS
ON ITEMS.ITEM = Arr1.ITEM)
GROUP BY ITEMS.ITEM
You seem to have made this much more complicated than you need to if your requirements are correct. From what you say you need
1) Items that arrived on 2019-01-01 and have not been shipped.
2) Items that arrived on 2019-01-01 and were shipped on the same day.
There's a number of issues with your current SQL, but you can get your result much more simply with a simple OR clause:
SELECT A.ITEM, COUNT(*)
FROM ITEMS A
WHERE A.DT_ARRIVAL = '2019-01-01'
AND ( NOT EXISTS (SELECT 1 FROM ISSUED B WHERE A.ID = B.ID)
OR (EXISTS (SELECT 1 FROM ISSUED C WHERE A.ID = C.ID
AND C.ISSUED_DT = A.DT_ARRIVAL)
)
GROUP BY A.ITEM
Let me know if I've missed something, but that seems to cover the requirements as listed.
One of the problems with your current query is you're doing a left join from rows that meet your first requirement, to rows that meet your second. They should never join and so you'll never get the rows/values for your second requirement. Overall I think you've confused yourself using too many unnecessary sub-queries and joins, and then made it even more confusing by not using unique alias's when you've referenced the same table multiple times.

SQL many-to-many, how to check criteria on multiple rows

In many-to-many table, how to find ID where all criteria are matched, but maybe one row matches one criterion and another row matches another criterion?
For example, let's say I have a table that maps shopping carts to products, and another table where the products are defined.
How can I find a shopping cart that has at least one one match for every criterion?
Criteria could be, for example, product.category like '%fruit%', product.category like '%vegetable%', etc.
Ultimately I want to get back a shopping cart ID (could be all of them, but in my specific case I am happy to get any matching ID) that has at least one of each match in it.
I am assuming a table named cart_per_product with fields cart,product, and a table named product with fields product,category.
select cart from cart_per_product c
where exists
(
select 1 from product p1 where p1.product=c.product and p1.category like N'%fruit%'
)
and exists
(
select 1 from product p2 where p2.product=c.product and p2.category like N'%vegetable%'
)
You can use ANY and ALL operators combined with outer joins. A simple sample on a M:N relation:
select p.name
from products p
where id_product = ALL -- all operator
( select pc.id_product
from categories c
left outer join product_category pc on pc.id_product = p.id_product and
pc.id_category = c.id_category
)
I think you can figure out the column names
select c.id
from cart c
join product p
on c.pID = p.ID
group by c.id
having count(distinct p.catID) = (select count(distinct p.catID) from product)
Generic approach that possibly isn't the most efficient:
with data as (
select *,
count(case when <match condition> then 1 end)
over (partition by cartid) as matches
from <cart inner join products ...>
)
select * from data
where matches > 0;

Selecting maximum value and inserting 0 if there are no entries

I have two tables, items and bids.
create table items (
id serial primary key,
store_id int,
min_bid int
);
create table bids (
item_id int,
amount int
);
I want to select items and include information about the max bid.
select items.*, max(bids.amount) from items
join bids on bids.item_id = items.id
where items.store_id = $store_id
group by items.id
However, when there are no bids for a particular item, the item just doesn't get selected. How can I make it so that when there are no bids, the item still gets selected and fills in the max(bids.amount) column with items.min_bid? (Or 0 is fine, too.)
I tried this:
select items.*, coalesce(max(bids.amount), items.min_bid) from items
join bids on bids.item_id = items.id
where items.store_id = $store_id
group by items.id
which doesn't work. I'm assuming it's because of the join that the items aren't getting selected in the first place.
What should I do?
The two crucial elements are LEFT JOIN and COALESCE().
#Adrian already commented on LEFT [OUTER ]JOIN. It preserves all rows at the left hand of the join and fills missing columns to the right with NULL values. The manual has more on the basics.
COALESCE() replaces NULL values with the provided alternative - 0 in this case.
SELECT i.*, COALESCE(max(b.amount), 0)
FROM items i
LEFT JOIN bids b ON b.item_id = i.id
WHERE i.store_id = $store_id
GROUP BY i.id
This alternative form is often faster when large parts of the sub-table are used: Aggregate in a subquery first, join later. This way you don't need an aggregation in the outer query. The second query also demonstrates how you can supply items.min_bid as replacement for NULL values.
SELECT i.*, COALESCE(b.max_amount, i.min_bid)
FROM items i
LEFT JOIN (
SELECT item_id, max(amount) AS max_amount
FROM bids
) b ON b.item_id = i.id
WHERE i.store_id = $store_id;
First, to display the items with no bids, you have to make use of a LEFT JOIN:
select items.*, max(bids.amount)
from items
left join bids on bids.item_id = items.id
where items.store_id = $store_id
group by items.id

Finding if there's at least one attribute in common in a set

I'm trying to do a validation using an Oracle SQL query, and the idea is:
There are items, and to every item there could be more than one suppliers.
I have a set of items (in a shopping bag) and want to verify if there is at least one common supplier between them. That means that all the items in the bag share at least one supplier.
Simplifying, the tables are:
BAG table with bag_id and item columns.
SUPPLY table with item and supplier columns.
Thank you!
This should give you a list of suppliers that match more than one item, with the most common suppliers on top
SELECT SUPPLY.supplier, COUNT(SUPPLY.item) item_count
FROM BAG
JOIN SUPPLY
ON BAG.item = SUPPLY.item
GROUP BY SUPPLY.supplier
HAVING COUNT(SUPPLY.item) > 1
WHERE BAG.bag_id = ? --substitute bag_id
ORDER BY COUNT(SUPPLY.item) DESC
If you need to find suppliers that match ALL items in the bag, then use this query:
SELECT SUPPLY.supplier
FROM BAG
JOIN (SELECT bag_id, COUNT(*) as item_count FROM BAG GROUP BY bag_id WHERE bag_id = ?) bag_count
ON BAG.id = bag_count.bag_id
JOIN SUPPLY
ON BAG.item = SUPPLY.item
GROUP BY SUPPLY.supplier
HAVING COUNT(SUPPLY.item) = bag_count.item_count
WHERE BAG.bag_id = ?
You can check if the list is empty to determine if no common suppliers exist by wrapping this with an EXISTS statement.
select case when exists
(
select 1
from bag b
inner join item i on i.id = b.item_id
inner join supplier s on s.id = i.supplier_id
and s.is_main = 'Y'
)
then 'Y'
else 'N'
end contains_main_supplier
from dual

SQL Server - Calculated column Sum from different table

I have two tables (Items and Item_Types), and I want to do something like the following as a formula in a calculated column in the Item_Types table:
SELECT SUM(Items.Qty_In_Stock) FROM Items WHERE Items.Item_Type = Item_Types.ID
But Management Studio isn't liking it.
How can I do this? Or have I just got my syntax wrong? I'm not great with SQL.
Thanks
EDIT: I think I need to be more specific.
One ItemType can have many Items.
Each Item has a Qty_In_Stock field.
I want to get a sum of all Qty_In_Stock fields where Items.Item_Type = Item_Types.ID - not just a sum of the entire column.
Cheers
Assuming you are trying to show the available qty for each item type:
I would recommend creating a view that shows this information:
You will need to "GROUP BY" the item type.
CREATE VIEW Item_Types_Qty_In_Stock
AS
SELECT it.ID, SUM(i.Qty_In_Stock) AS QtyInStock FROM Items i
INNER JOIN Item_Types it ON i.Item_Type = it.ID
GROUP BY it.ID
GO
Once you have created the view, you can query it as if it were a table:
SELECT * FROM Item_Types_Qty_In_Stock
There is no join for the Item_types table. Put a join and you should be OK.
SELECT SUM(Items.Qty_In_Stock) from Items INNER JOIN Item_Types ON Items.Item_Type = Item_Types.ID GROUP BY Items.Item_Type
Below will return the sum of Qty_In_Stock for all items with at least one Item_type record.
SELECT
SUM(i.Qty_In_Stock)
FROM Items i
INNER JOIN Item_types it on i.Item_Type = it.ID
You may want to use a left join if every item does not have at least one Item_type record Example:
SELECT
SUM(i.Qty_In_Stock)
FROM Items i
LEFT JOIN Item_types it on i.Item_Type = it.ID
Edited
Perhaps this is what you want. This will return an individual sum of Items(qty_in_stock) for each Item_type ID
SELECT
it.Id,
SUM(i.Qty_In_Stock)
FROM Items i
INNER JOIN Item_types it on i.Item_Type = it.ID
GROUP BY it.Id
A computed column is computed from an expression that can use other columns in the same table. The expression can be a noncomputed column name, constant, function, and any combination of these connected by one or more operators. The expression cannot be a subquery.