Selecting maximum value and inserting 0 if there are no entries - sql

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

Related

Access join of two subqueries

I am constructing a simple database for an inventory system, with a barcode reader. I have one form to add inventory, and one form to remove inventory. Each adjust add a column to the add or remove table.
I made queries for my add and remove that return the total value of each item, associated with the id for each item. I want to join my add and remove tables so I can calculate the difference for a current quantity.
I am running into trouble with joining my add subquery and my remove subquery.
Add:
SELECT Items.description, Count(Trans_Add.s) AS Quantity
FROM Items INNER JOIN Trans_Add ON Items.ID = Trans_Add.s
GROUP BY Items.description;
Remove:
SELECT Items.description, Count(Trans_Remove.s) AS Quantity
FROM Items INNER JOIN Trans_Remove ON Items.ID = Trans_Remove.s
GROUP BY Items.description;
Whenever I try to assign alias for either in the from clause of my new query and join them it does not work.
Try:
SELECT Items.ID, Items.Description, (Items.TotalCount + A.Quantity - B.Quantity) as OnHandQty
FROM Items INNER JOIN
(SELECT Items.ID, Count(Trans_Add.s) AS Quantity
FROM Items INNER JOIN Trans_Add ON Items.ID = Trans_Add.s
GROUP BY Items.description
) A
ON Items.ID = A.ID
INNER JOIN (
SELECT Items.description, Count(Trans_Remove.s) AS Quantity
FROM Items INNER JOIN Trans_Remove ON Items.ID = Trans_Remove.s
GROUP BY Items.description
) B
ON Items.ID = B.ID
I made up a Field named TotalCount because you did not mentioned what is the Item Quantity in Items table. So, change it accordingly, that is, according to the
right field name for Item Quantity from Items table.

How to make a conditional join based on column value

I have a tables:
Activities{ActivityId, ActivityTypeId, CreatedByUserId, ItemId, FollowId}
Items{ItemId, Title}
Followings{FollowId, FollowerId, FollowingId}
In Activity table ItemId or FollowId can be null (never both).
Now I need somehow based on or ActivityTypeId or if one of ItemId or FollowId if null to make join to Items table or Followings table.
How to write this kind of conditional query?
If ItemId is null I need to made join to Followings table.
If ItemId is not null I need to made join to Items table.
I also asked about ActivityTypeId because it's maybe easier to check activity type and based on that to make a join to Items or Followings table.
Each of the following will return different results. The first will return every relationship between (actvities and items) and (activities and followings). The second will return every combination of relationship between (actvities and items) and (activities and followings). Try them both and see which works best for you.
(Note: I haven't tested these - I wrote them freehand - there may be syntax errors)
SELECT a.ActivityId,
a.ActivityTypeId,
a.CreatedByUserId,
i.ItemId,
i.Title,
null as FollowId,
null as FollowerId,
null as FollowingId
FROM Activities a INNER JOIN Items i ON a.ItemId = i.ItemId
UNION ALL
SELECT a.ActivityId,
a.ActivityTypeId,
a.CreatedByUserId,
null as ItemId,
null as Title,
f.FollowId,
f.FollowerId,
f.FollowingId
FROM Activities a INNER JOIN Followings f ON a.FollowId = f.FollowId
OR:
SELECT a.ActivityId,
a.ActivityTypeId,
a.CreatedByUserId,
i.ItemId,
i.Title,
f.FollowId,
f.FollowerId,
f.FollowingId
FROM Activities a
LEFT OUTER JOIN Items i ON a.ItemId = i.ItemId
LEFT OUTER JOIN Followings f ON a.FollowId = f.FollowId

SQL Nested Queries with Parent Parameters

I need to write a query of the scenario but I am lost writing it.
Assume there are two tables Items and Bids. The items are being selected via some sort of filter
SELECT i.* FROM Items i WHERE i.Id = 2
Now there is a Bids table having "ItemId" column to link Items to Bids. Now I want all items' data with HighestBid, LowestBid and TotalBids and I am trying this but it's not working.
SELECT i.*, hal.*
FROM Items i, (SELECT MAX(b.OfferAmount), MIN(b.OfferAmount), COUNT(b.*) FROM Bids b WHERE b.ItemId = i.Id) As hal
WHERE i.Id = 2
Is there something wrong with this?
Try this
SELECT i.*,
hal.*
FROM items i
INNER JOIN (SELECT MAX(b.offeramount),
MIN(b.offeramount),
b.itemid,
COUNT(b.*)
FROM bids b
GROUP BY itemid) AS hal
ON i.Id= hal.itemid
WHERE i.id = 2

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.

Beginner multi-join question

There are 3 tables for a small trading system; USERS, ITEMS and TRADES, the trades themselves are held in table TRADES (logically enough), as:
column type
========== =====
seller_id int
buyer_id int
item_id int
quantity int
cost int
currency int
I want to display pending trades, using a 3-way join to map IDs to names in tables USER and ITEM. So far I've got:
SELECT
users.name AS seller,
items.id AS item_id,
items.name AS item,
trades.item_id AS trade_id,
trades.quantity AS quantity,
trades.cost AS cost,
trades.currency AS currency
FROM
(trades INNER JOIN users ON trades.seller_id = users.id)
INNER JOIN items ON trades.item_id = items.id
WHERE trades.buyer_id = xxx
The first join works fine, but as soon as I pass the result to the second it fails; returns zero data. I'm braced for it being a real noobie problem (in fact I'm hoping it is, easy to fix = good) but I can't see it (probably because I am in fact a noob at these things).
SELECT
users.name AS seller,
items.id AS item_id,
items.name AS item,
trades.item_id AS trade_id,
trades.quantity AS quantity,
trades.cost AS cost,
trades.currency AS currency
FROM
trades
LEFT JOIN
users
ON
trades.seller_id = users.id
LEFT JOIN
items
ON
trades.item_id = items.id
WHERE trades.buyer_id = xxx
I think trades_id needs to be trades.item_id
I also advise you to use LEFT JOIN instead of INNER JOIN, only use INNER JOIN when you want to trigger a error if there's no match. When you use LEFT JOIN it always returns data, also when there's no match in the second table. See: http://www.w3schools.com/Sql/sql_join.asp