Working SQL Server 2005 Query Optimization - sql

I have two tables Sell and Purchase. My query is giving me the desired result, I am carefull about it performance so plz guide me if it can be better. My tables are:
Table Sell
UserId | ProductId | ProductName | ProductPrice
1 | p_101 | Cycle | 500
1 | p_121 | Car | 500000
2 | p_111 | Cycle | 5000
Table Purchase
UserId | ProductId | ProductName | ProductPrice
1 | p_109 | CellPhone | 150
2 | p_121 | Car | 500000
3 | p_111 | Book | 15
Desired OutPut Table
Type | ProductId | ProductName | ProductPrice
Sell | p_101 | Cycle | 500
Sell | p_121 | Car | 500000
Purchase| p_109 | CellPhone | 150
Working Query:
SELECT type, P1.ProductId, P1.ProductName, P1.ProductPrice
FROM
(
SELECT s.UserId, 'Sell' as type, s.ProductId, s.ProductName, s.ProductPrice FROM [Sell] s
UNION
SELECT p.userid, 'Purchase' as type, p.ProductId, p.ProductName, p.ProductPrice FROM [Purchase] p
) as P1
WHERE userid=1

Better design is to combine both tables and have a transaction_type column which will either have "Purchase" or "Sell" as values. If you do that you won't have to do UNION or UNION ALL.
With current design here is a simple and faster way to get records. Note that I have used UNION ALL which is faster than UNION as UNION uses DISTINCT to unique records which I think in your case doesn't apply. If you provide details about the index and execution plan I can see if there is a better way.
SELECT s.userid,
'Sell' as type,
s.ProductId,
s.ProductName,
s.ProductPrice
FROM Sell s
WHERE UserId = 1
UNION ALL
SELECT p.userid,
'Purchase' as type,
p.ProductId,
p.ProductName,
p.ProductPrice
FROM Purchase P
WHERE UserId = 1

Its better to use joins rather than subqueries. This way, there will be no overhead on your queries specially on dealing with large volumes of data.

Related

SQL - select values from two different tables

I have got a problem with selecting values from two different tables. Tables below:
Material:
MaterialID | MaterialName
-------------------------
1111111 | Material1
2222222 | Material2
3333333 | Material3
Stock:
MaterialID | Location | Quantity
---------------------------------
1111111 | LocA | 10
1111111 | LocB | 20
2222222 | LocC | 15
2222222 | LocD | 10
My SQL query below:
SELECT
[Material].[MaterialName] as 'Material Name',
custom.quantity as 'Total Quantity',
FROM
[Material]
inner join (
SELECT
[MaterialId] as 'Materialcode',
SUM([Quantity]) as 'Quantity'
from
[Stock]
group by
[MaterialId]
) custom
on
custom.Materialcode = [Material].[MaterialId]
The result is:
Material Name | Total Quantity
------------------------------
Material1 | 30
Material2 | 25
The problem is that in the result there is no information about Material3 (I know that the quantity is equal to 0 as it`s not in Stock table, but I need a result that will show all of the materials - like below:
Material Name | Total Quantity
------------------------------
Material1 | 30
Material2 | 25
Material3 | 0
Is it possible?
You can left join and aggregate:
select m.materialName, coalesce(sum(s.quantity), 0) total_quantity
from material m
left join stock s on s.materialID = m.materialID
group by m.materialID, m.materialName
You may also aggregate, then left join (that was your original attempt, you just need to change the join type).
Actually, you might as well use a correlated subquery - with an index on stock(materialID, quantity), this may be an efficient solution (and you don't need coalesce() here):
select
m.materialName,
(select sum(quantity) from stock s where s.materialID = m.materialID) total_quantity
from material m
Another way to express this is to use a lateral join:
select m.materialName, s.total_quantity
from material m
outer apply (select sum(quantity) total_quantity from stock s where s.materialID = m.materialID) s

Oracle - Count condition case

I am facing a problem with a query. My goal is to get all product names, unit of mass, quantity and number of pallets they are located in. The problem is with number of pallets of item.
QUERY:
SELECT "Product_Name"
, "Unit_of_Mass"
, SUM("Quantity_Per_UOM")
, Count(*) as "Number_Of_Pallet"
FROM
(select p.prod_desc as "Product_Name"
, s.quantity as "Quantity_Per_UOM"
, u.description as "Unit_of_Mass"
, s.container_id
, s.product_id
from wms_stock s
join wms_product p on p.product_id = s.product_id
join wms_uom u on p.uom_base_id = u.uom_id
)
group by "Product_Name", "Unit_of_Mass"
It almost works. The problem is I need to do some condition in Count(*) (that's what I think should be done). In table wms_stock I got product_id and container_id, and when in some row they are same it should count the number of pallets as 1 but still add the quantities.
So from first select:
Product_Name | Quantity | UnitOfMass | ContainerId | ProductId
A | 2 | kg | 10 | 11
A | 1 | kg | 10 | 11
B | 2 | kg | 11 | 12
I should get result
Product_Name | Quantity_Per_UOM | UnitOfMass | Number_Of_Pallet
A | 3 | kg | 1
B | 2 | kg | 1
You can try below condition in your select list -
COUNT(DISTINCT ContainerId || ProductId)
Just for your information, || is not an operator rather it is concatenation operator in Oracle. So i have just concat both the columns and picked up the distinct from them.

PostgreSQL Referencing Outer Query in Subquery

I have two Postgres tables (really, more than that, but simplified for the purpose of the question) - one a record of products that have been ordered by customers, and another a historical record of prices per customer and a date they went into effect. Something like this:
'orders' table
customer_id | timestamp | quantity
------------+---------------------+---------
1 | 2015-09-29 16:01:01 | 5
1 | 2015-10-23 14:33:36 | 3
2 | 2015-10-19 09:43:02 | 7
1 | 2015-11-16 15:08:32 | 2
'prices' table
customer_id | effective_time | price
------------+---------------------+-------
1 | 2015-01-01 00:00:00 | 15.00
1 | 2015-10-01 00:00:00 | 12.00
2 | 2015-01-01 00:00:00 | 14.00
I'm trying to create a query that will return every order and its unit price for that customer at the time of the order, like this:
desired result
customer_id | quantity | price
------------+----------+------
1 | 5 | 15.00
1 | 3 | 12.00
2 | 7 | 14.00
1 | 2 | 12.00
This is essentially what I want, but I know that you can't reference an outer query inside an inner query, and I'm having trouble figuring out how to re-factor:
SELECT
o.customer_id,
o.quantity,
p.price
FROM orders o
INNER JOIN (
SELECT price
FROM prices x
WHERE x.customer_id = o.customer_id
AND x.effective_time <= o.timestamp
ORDER BY x.effective_time DESC
LIMIT 1
) p
;
Can anyone suggest the best way to make this work?
Instead of joining an inline view based on the prices table, you can perform a subquery in the SELECT list:
SELECT customer_id, quantity, (
SELECT price
FROM prices p
WHERE
p.customer_id = o.customer_id
AND p.effective_time <= o.timestamp
ORDER BY p.effective_time DESC
LIMIT 1
) AS price
FROM orders o
That does rely on a correlated subquery, which could be bad for performance, but with the way your data are structured I doubt there's a substantially better alternative.
You dont need the subquery, just a plain inner join will do (this assumes there are no duplicate effective_times per customer):
SELECT o.customer_id, o.quantity
,p.price
FROM orders o
JOIN prices p ON p.customer_id = o.customer_id
AND p.effective_time <= o.timestamp
AND NOT EXISTS ( SELECT * FROM prices nx
WHERE nx.customer_id = o.customer_id
AND nx.effective_time <= o.timestamp
AND nx.effective_time > p.effective_time
)
;

How can I sum two rows from two different tables?

I am facing a problem in sql query. I am attaching the Schema.
The thing i want is. I want to generate a query that will produce result like below.
=======================================================================================
showroom_id | total_salary | total_expense | total_sold | balance
=======================================================================================
| 1 | 2000 | 8000 | 30000 | 20000
=======================================================================================
| 2 | 1000 | 4000 | 25000 | 20000
=======================================================================================
| 3 | 3000 | 7000 | 30000 | 20000
====================================================================================
I want to group by showroom id and sum the expense amount , staff salary, item's price and show them in each individual row. Then another column balance will show total_sold -( total_expanse + total_salary). How can I do the query?
select
s.id as showroom_id,
sal.amount as total_salary,
exp.amount as total_expense
-- not sure where to get total_sold amount?
from showroom as s
left outer join (
select sum(t.amount) as salary, t.showroom_id
from staff_salary as t
group by t.showroom_id
) as sal on sal.showroom_id = s.id
left outer join (
select sum(t.amount) as salary, t.showroom_id
from expense as t
group by t.showroom_id
) as exp on exp.showroom_id = s.id

select from two tables and conditionally collapse one column

I have a list of Products
upc | name | price | qty
----------------------------
1 | apple | 1.00 | 3
2 | peach | 2.00 | 7
3 | melon | 1.75 | 2
and SaleProducts
upc | price
------------
2 | 1.90
I want to select from Products but also sale price
from SaleProducts (if product is on sale). This is what I came up with:
SELECT t1.upc, t1.name, MIN(t1.price) AS 'price', t1.qty
FROM (
SELECT p.upc, p.name, p.price, p.qty
FROM products p
UNION
SELECT sp.upc, NULL, sp.price, NULL
FROM saleproducts sp
) t1
GROUP BY t1.upc;
Output:
upc | name | price | qty
----------------------------
1 | apple | 1.00 | 3
2 | peach | 1.90 | 7
3 | melon | 1.75 | 2
Can anyone suggest a more elegant way to accomplish this? Im aware of similar question but my goal is to grab whichever price is lower, so COALESCE wouldn't work.
The only restriction is, I must use vanilla SQL, no stored procs or IF's.
Try this instead using CASE:
SELECT p.upc, p.name,
CASE WHEN sp.price IS NOT NULL
THEN CASE WHEN p.price > sp.price
THEN sp.price
ELSE p.price
END
ELSE p.price
END price, p.qty
FROM products p LEFT JOIN saleproducts sp ON p.upc = sp.upc;
It will prefer to use the price from saleproducts when it's available. If there is not a sale price for that product it will instead use the price from products.
EDIT - I've updated my answer so that it always gets the lowest price. FWIW I can't imagine why you'd bother having a sale price which is actually higher than your list price.
This seems more like the job for a left outer join:
SELECT p.upc, p.name,
(case when sp.price is null or p.price < sp.upc then p.price
else sp.price
end) as price,
p.qty
FROM products p left outer join
salesproducts sp
on p.upc = sp.upc;