Select fields based on aggregates results - mysql

I have a typical products table with the fields name and price. I have to select the sum of all the prices, the name of the cheapest product and the name of the most expensive product and then return it all in the same result set. I've tried some combinations but the best I could come up with was an ugly query with multiple nested subselects. Can anyone help me with a good query example, please? Thanks in advance.
To illustrate the problem, here is a minimalistic products table:
+----+-------+------------------+
| id | price | name |
+----+-------+------------------+
| 1 | 2.20 | Shack Beer |
| 2 | 3.40 | Freeze IPA |
| 3 | 1.10 | Poor Man's Ale |
| 4 | 3.40 | Alabama Sour |
| 5 | 7.20 | Irish Stout |
+----+-------+------------------+
Given the table above, the query must return the following result set:
total_pricing = 17.30
cheapest_product = Poor Man's Ale
most_expensive_product = Irish Stout

Here are a couple of options for you (SQL Fiddle):
SELECT SUM(p.price) AS total_pricing,
(
SELECT name
FROM products
ORDER BY price
LIMIT 1
)AS cheapest_product,
(
SELECT name
FROM products
ORDER BY price DESC
LIMIT 1
)AS most_expensive_product
FROM products p;
Or in separate rows:
SELECT 'total_pricing' as Category, SUM(p.price) AS total_pricing
FROM products p
UNION
(
SELECT 'cheapest_product', name
FROM products
ORDER BY price
LIMIT 1
)
UNION
(
SELECT 'most_expensive_product', name
FROM products
ORDER BY price DESC
LIMIT 1
);

Check SQL Fiddle
SELECT DISTINCT price,
name
FROM products AS p1,
(SELECT MAX(price) AS `most_expensive_product`,
MIN(price) AS `cheapest_product`
FROM products) AS p2
WHERE p2.most_expensive_product = p1.price
OR p2.cheapest_product = p1.price
UNION
SELECT sum(price) AS price,
"Total_pricing" AS name
FROM products;

Related

Joining multiple select sql statements (4 tables)

I need to join together 2 SQL statements and both of those statements work on their own. But I don't know how to combine both into 1 SQL statement.
I have two tables in 1st statement, TR120 and TR1201.
The SQL is this:
select
PRODUCT, PRICE, QUANTITY, INVOICE.DATE
from
TR1201
left join
(select
DATE, ID as INVOICE_ID, INVOICE
from TR120) as INVOICE on INVOICE.INVOICE_ID = ID
where
INVOICE.DATE >= '2016-06-01' and INVOICE.DATE <= '2016-06-30'
This returns a list of all the products I sold, with price, quantity and date of sales in a specific time frame from 01-06-16 till 30-06-16.
Now I need to find out the latest price that I bought product for in different two tables TR100 and TR1001 based on the product and date of sale from the 1st SQL statement.
select
PRODUCT, PRICE, SUP.DATE
from
TR1001
left join
(select
DATE, ID as SUP_ID, SUP_INVOICE
from TR100) as SUP on SUP.SUP_ID = ID
This returns a list of all the products that I have bought with a price and a date. I only need last record from this query based on product and date of purchased.
TR120
ID | INVOICE | DATE
1 | 000001 |2016-06-05
2 | 000002 |2016-06-15
3 | 000003 |2016-06-25
TR1201
ID | PRODUCT | PRICE A | QUANTITY
1 | A | 2,00 | 5
2 | A | 2,00 | 2
3 | A | 2,00 | 1
TR100
ID | SUP_INVOICE | DATE
1 | 160001 | 2016-05-30
2 | 160002 | 2016-06-16
TR1001
ID | PRODUCT | PRICE B
1 | A | 0,5
2 | A | 0,7
The result I am trying to get is this:
PRODUCT | PRICE A (tr1201) | QUANTITY | DATE (tr100) | PRICE B (tr1001)
A | 2 | 5 | 2016-05-30 | 0,5
A | 2 | 2 | 2016-05-15 | 0,5
A | 2 | 1 | 2016-05-16 | 0,7
That is all I want to do :(
Have you tried first_value?
FIRST_VALUE ( [scalar_expression ] )
OVER ( [ partition_by_clause ] order_by_clause [ rows_range_clause ] )
it works like this:
select distinct id,
first_value(price) over (partition by id (,sup) order by date DESC (latest, ASC for oldest)) as last_price
from table;
Documentation can be found here: https://msdn.microsoft.com/en-us/library/hh213018.aspx
I don't have your tables so cannot test and therefore am providing advice only.
I think what you need is an Outer apply like this instead of joins
select
T1.Product
, T1.Price
, T2.DATE -- Alias this
, T2.Price -- Alias this
, T3.DATE -- Alias this
, T3.Price -- Alias this
from T1
OUTER APPLY (
select top 1
Date
,Price
from table2
WHERE ID = T1.Id AND product = T1.Product-- plus any other joins
ORDER BY Date desc
) as T2
OUTER APPLY (
select top 1
Date
,Price
from table3
WHERE ID = T1.Id AND product = T1.Product-- plus any other joins
ORDER BY Date desc
) as T3

SQL Select count of categories accross multiple columns

I have a data structure in the table of these columns
ID | Title | Category_level_1 | Category_level_2 | Category_level_3
1 | offer 1 | Browns | Greens | White
2 | offer 1 | Browns | White |
3 | offer 2 | Greens | Yellow |
4 | offer 3 | Browns | Greens |
5 | offer 4 | Browns | Yellow | White
Without the ability to change the table structure I need to "count the number for Offers per Category across the 3 columns"
There is also columns for date range of the offer, to limit to the current ones, but I want to work out the query first.
I need to get a list of all the Categories and then put offers against them.
Offer can be in the table more than once.
As far as I have got is do a temp table first with a UNION.
CREATE TEMPORARY TABLE IF NOT EXISTS Cats AS
( SELECT DISTINCT(opt) FROM (
SELECT Category_level_1 AS opt FROM a_table
UNION
SELECT Category_level_2 AS opt FROM a_table
UNION
SELECT Category_level_3 AS opt FROM a_table
) AS Temp
) ;
SELECT
Cats.opt AS "Joint Cat",
(
SELECT count(*)
FROM a_table
WHERE a_table.`Category_level_1` = Cats.opt
OR a_table.`Category_level_2` = Cats.opt
OR a_table.`Category_level_3` = Cats.opt
GROUP BY a_table.Title
) As Total
FROM Cats
WHERE Category_level_1 != ''
ORDER BY Category_level_1 ASC;
ISSUE:
a) so the union works well and I get my values. DONE
b) the Total subselect though is not grouping correctly.
I just want a count of all the rows returned but it is grouping with a count of the row titles not all rows.
So trying to work out how to figure this should work and the SQL could be totally different with the answer:
Joint Category | Total Count of offers
Browns | 3
White | 3
Greens | 2
Yellow | 2
plan
take a union of all distinct categories, alias to Joint Category
aggregate count over Joint Category ( where not null or blank - not clear from your rendering if those fields are null or blank.. )
grouping by Joint Category
query
select `Joint Category`, count(*) as `Total Count of offers`
from
(
select Title, Category_level_1 as `Joint Category`
from a_table
union
select Title, Category_level_2
from a_table
union
select Title, Category_level_3
from a_table
) allcats
where `Joint Category` is not null
and `Joint Category` <> ''
group by `Joint Category`
;
output
+----------------+-----------------------+
| Joint Category | Total Count of offers |
+----------------+-----------------------+
| Browns | 3 |
| Greens | 3 |
| White | 2 |
| Yellow | 2 |
+----------------+-----------------------+
sqlfiddle
Your results are a bit confusing . . . I cannot tell why browns and whites both have a count of 3. I think you are counting the combination of level and category.
I would be inclined to approach this using union all and then use count() or count(distinct), depending on what the counting logic really is. For the combination of level and category:
SELECT cat, COUNT(DISTINCT level, title) as numtitles
FROM ((SELECT title, 1 as level, category_level1 as cat FROM a_table) union all
(SELECT title, 2 as level, category_level2 as cat FROM a_table) union all
(SELECT title, 3 as level, category_level3 as cat FROM a_table)
) tc
WHERE cat is not null
GROUP BY cat;
You can include the date column in each of the subqueries and then include a condition in the WHERE clause.

Selecting the most recent, lowest price from multiple vendors for an inventory item

I’m fairly proficient at SQL, however this question has had myself stumped for quite a while now. In the most basic sense, there are simply two tables:
Items
+----+--------+
| id | title |
+----+--------+
| 1 | socks |
| 2 | banana |
| 3 | watch |
| 4 | box |
| 5 | shoe |
+----+--------+
...and the prices table:
Prices
+---------+-----------+-------+------------+
| item_id | vendor_id | price | created_at |
+---------+-----------+-------+------------+
| 1 | 1 | 5.99 | Today |
| 1 | 2 | 4.99 | Today |
| 2 | 1 | 6.99 | Today |
| 2 | 2 | 6.99 | Today |
| 1 | 1 | 3.99 | Yesterday |
| 1 | 1 | 4.99 | Yesterday |
| 2 | 1 | 6.99 | Yesterday |
| 2 | 2 | 6.99 | Yesterday |
+---------+-----------+-------+------------+
(Please note: created_at is actually a timestamp, the words “Today” and “Yesterday” were provided merely to quickly convey the concept).
My goal is to get a simple result back containing the inventory item associated with the most recent, lowest price, including the reference to the vendor_id who is providing said price.
However, I find the stumbling block appears to be the sheer number of requirements for the statement (or statements) to handle:
Each item has multiple vendors, so we need to determine which price between all the vendors for each item is the lowest
New prices for the items get appended regularly, therefore we only want to consider the most recent price for each item for each vendor
We want to roll all that up into a single result, one item per row which includes the item, price and vendor
It seems simple, but I’ve found this problem to be uncanningly difficult.
As a note, I’m using Postgres, so all the fanciness it provides is available for use (ie: window functions).
Much simpler with DISTINCT ON in Postgres:
Current price per item for each vendor
SELECT DISTINCT ON (p.item_id, p.vendor_id)
i.title, p.price, p.vendor_id
FROM prices p
JOIN items i ON i.id = p.item_id
ORDER BY p.item_id, p.vendor_id, p.created_at DESC;
Optimal vendor for each item
SELECT DISTINCT ON (item_id)
i.title, p.price, p.vendor_id -- add more columns as you need
FROM (
SELECT DISTINCT ON (item_id, vendor_id)
item_id, price, vendor_id -- add more columns as you need
FROM prices p
ORDER BY item_id, vendor_id, created_at DESC
) p
JOIN items i ON i.id = p.item_id
ORDER BY item_id, price;
->SQLfiddle demo
Detailed explanation:
Select first row in each GROUP BY group?
Try this
CREATE TABLE #Prices ( Iid INT, Vid INT, Price Money, Created DateTime)
INSERT INTO #Prices
SELECT 1, 1, 5.99 ,GETDATE() UNION
SELECT 1, 2, 4.99 ,GETDATE() UNION
SELECT 2, 1, 6.99 ,GETDATE() UNION
SELECT 2, 2, 6.99 ,GETDATE() UNION
SELECT 1, 1, 3.99 ,GETDATE()-1 UNION
SELECT 1, 2, 4.99 ,GETDATE()-1 UNION
SELECT 2, 1, 6.99 ,GETDATE()-1 UNION
SELECT 2, 2, 6.99 ,GETDATE()-1
WITH CTE AS
(
SELECT
MyPriority = ROW_NUMBER() OVER ( partition by Iid, Vid ORDER BY Created DESC, Price ASC)
, Iid
, Vid
, price
, Created
FROM #Prices
)
SELECT * FROM CTE WHERE MyPriority = 1
It's also possible to do this with windowed functions, it will work on SQL Server version > 2005:
with cte1 as (
select
*,
row_number() over(partition by vendor_id, item_id order by created_at desc) as row_num
from prices
), cte2 as (
select
*,
row_number() over(partition by item_id order by price asc) as row_num2
from cte1
where row_num = 1
)
select i.title, c.price, c.vendor_id
from cte2 as c
inner join items as i on i.id = c.item_id
where c.row_num2 = 1;
sql fiddle demo(Thanks Erwin)

Getting the value of row with min(price) group by code in mysql

My table structure is like this:
country | price | code
---------------------------------------
EGYPT PROPER WHOLESALE | 0.037 | 20
EGYPT FIXED | 0.0710 | 20
EGYPT-OTHER | .0497 | 20
I have to use GROUP BY code and I want the output as:
country | price | code
---------------------------------------
EGYPT PROPER WHOLESALE | 0.037 | 20
having the minimum value of price.
And my query is:
select MIN(inp.price) as Price, inp.code, inp.country
from tbl_input_values as inp
GROUP BY code
but i am getting the output as:
country | price | code
------------------------------
EGYPT-OTHER | .037 | 20
here,
SELECT *
FROM tbl_input_values
WHERE price = (SELECT MIN(PRICE) FROM tbl_input_values)
SQLFiddle Demo
This will show the rows with the minimum price, for every code:
SELECT yourtable.*
FROM yourtable
WHERE (code, price) IN (select code, min(price)
from yourtable
group by code)
With multiple codes :
SELECT tb1.*
FROM tbl_input_values tb1
INNER JOIN (select code, min(price) minPrice
FROM tbl_input_values
GROUP BY code) as a
ON a.code = tb1.code and a.minPrice =tb1.price
I think the easiest way is:
select t.*
from t
order by price
limit 1
You don't need aggregation at all.

ORDER BY and GROUP BY of MySQL query

I have two mySQL tables as follows:
[product] table
P_id | Name | Quantity
1 | B | 10
2 | C | 15
3 | A | 8
[attribute] table
P_id | Name | Quantity
1 | Black | 5
1 | Red | 5
2 | Blue | 6
2 | Black | 9
How can I write an SQL query so that it can show the result from the above two tables as follows:
Report:
P_id | Name | Quantity
3 | A | 8
1 | B | 10
1 | Black | 5
1 | Red | 5
2 | C | 15
2 | Black | 9
2 | Blue | 6
These should be sorted on [Name] column, but these should be grouping on P_id column as above. By "grouping" on P_id, I mean "keeping all records with the same P_id next to each other". IS it possible to retrieve as above arrangement using a single SQL query.
SELECT P_id, Name, Quantity FROM (
SELECT P_id, Name, Quantity, Name as parent, 1 as level
FROM product
UNION
SELECT a.P_id, a.Name, a.Quantity, p.Name as parent, 2 as level
FROM attribute a JOIN product p ON a.P_id = p.P_id
) combined ORDER BY parent, level, Name
It should be Union operation, I believe like this:
select * from product union select * from attribute order Name;
Not sure why you wrote you need `group by' since your output is not grouped by any column.
Do you mean that you just need an union of both tables?
You can try this:
Select P_id, Name, Quantity
From product
Union
Select P_id, Name, Quantity
From attribute
Order by 2
SELECT * FROM
(
SELECT P_id, Name , Quantity FROM product
UNION ALL
SELECT P_id, Name , Quantity FROM attribute
) Order by Name
You will have to use grouping my friend. Then you can order on Both of you required columns like this:
SELECT * FROM product UNION SELECT * FROM attribute ORDER BY Name, P_id;
If you want them ordered by P_id and then Name, this should work.
(SELECT P_id, Name, Quantity FROM product)
UNION
(SELECT P_id, Name, Quantity FROM attribute)
ORDER BY P_id, Name;