SQL Server - Calculated column Sum from different table - sql

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.

Related

Deleting data from one table if the reference doesn't exist in two other tables

I managed to import too much data into one of my database tables. I want to delete most of this data, but I need to ensure that the reference doesn't exist in either of two other tables before I delete it.
I figured this query would be the solution. It give me the right result on a test database, but in the production environment it returns no hits.
select product
from products
where 1=1
and product not in (select product from location)
and product not in (select product from lines)
You are getting no results/hits it means that you table location and/or lines having the null values in the product column. in clause failed if column having null value.
try below query just added the null condition on the top of your shared query.
select product from products
where 1=1
and product not in ( select product from location where product is not null)
and product not in ( select product from lines where product is not null)
Use EXISTS instead of IN which is more efficient
DELETE FROM products WHERE
NOT EXISTS
(
SELECT
1
FROM [Location]
WHERE Product = Products.Product
) AND
NOT EXISTS
(
SELECT
1
FROM lines
WHERE Product = Products.Product
)
Try this..
DELETE FROM Products where not exists
(select 1 from Location
join lines on lines.Product = Location.Product
and Location.Product = Products.Product
);
It's difficult to tell from your post why the query would return results in the test database but not production other than there is different data or different structures. You might try including the DDL for the participating tables in your post so that we know what the table structures are. For example, is the "product" column a PK or a text name?
One thing that does jump out is that your query will probably perform poorly. Try something like this instead: (Assuming the "product" column is a PK in Products and FK in the other tables.)
Select product
From Products As p
Left Outer Join Location As l
On p.product = l.product
And l.product is null
Left Outer Join Lines as li
On p.product = li.product
And li.product is null;
This simple set based approach may help ...
DELETE p
FROM products p
LEFT JOIN location lo ON p.product = lo.product
LEFT JOIN lines li ON p.product = li.product
WHERE lo.product IS NULL AND li.product IS NULL

Get row from one table, plus COUNT from a related table

I'm trying to build an SQL query where I grab one table's information (WHERE shops.shop_domain = X) along with the COUNT of the customers table WHERE customers.product_id = 4242451.
The shops table DOES NOT have product.id in it, but the customers table DOES HAVE the shop_domain in it, hence my attempt to do some sort of join.
I essentially want to return the following:
shops.id
shops.name
shops.shop_domain
COUNT OF CUSTOMERS WHERE customers.product_id = '4242451'
Here is my not so lovely attempt at the query.
I think I have the idea right (maybe...) but I can't wrap my head around building this query.
SELECT shops.id, shops.name, shops.shop_domain, COUNT(customers.customer_id)
FROM shops
LEFT JOIN customers ON shops.shop_domain = customers.shop_domain
WHERE shops.shop_domain = 'myshop.com' AND
customers.product_id = '4242451'
GROUP BY shops.shop_id
Relevant database schemas:
shops:
id, name, shop_domain
customers:
id, name, product_id, shop_domain
You are close. The condition on customers needs to go in the ON clause, because this is a LEFT JOIN and customers is the second table:
SELECT s.id, s.name, s.shop_domain, COUNT(c.customer_id)
FROM shops s LEFT JOIN
customers c
ON s.shop_domain = c.shop_domain AND c.product_id = '4242451'
WHERE s.shop_domain = 'myshop.com'
GROUP BY s.id, s.name, s.shop_domain;
I am also inclined to include all three columns in the GROUP BY, although Postgres (and ANSI/ISO standards) are happy with just id if it is declared as the primary key in the table.
A correlated subquery should be substantially cheaper (and simpler) for the purpose:
SELECT id, name, shop_domain
, (SELECT count(*)
FROM customers
WHERE shop_domain = s.shop_domain
AND product_id = 4242451) AS special_count
FROM shops s
WHERE shop_domain = 'myshop.com';
This way you only need to aggregate in the subquery, and need not worry about undesired effects on the outer query.
Assuming product_id is a numeric data type, so I use a numeric literal (4242451) instead of a string literal '4242451' - which might cause problems otherwise.

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.

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

MySQL - conditional SELECT

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