SQL Query: finding cheapest car by company - sql

Following YouTube tutorial "Learn PostgreSQL Tutorial - Full Course for Beginners", I replicate teacher's code but yields different result and cannot figure out why.
Table is this simple:
id | make | model | price
-----+------------+--------------+------------
1 | Toyota | Matrix | 25451.36
and so on, 1000 entries.
Querying cheapest model from manufacturer, tutorial says:
SELECT
make, model, MIN(price)
FROM
car
GROUP BY
make, model;
And it works properly, returning as many entries as distinct car makers.
But when I run it in my PostgreSQL terminal returns all 1000 entries disordered.
However, when I query without model's name, I get the right answer, but (obviously)
without the model name as shown below:
make | cheapest
---------------+----------
Fillmore | 72263.48
McLaren | 78510.84
Any suggestions as to why this might happen?

This db-fiddle works as expected. Notice the output. It shows a proper GROUP BY.
Query source:
CREATE TABLE t (
make varchar(40),
model varchar(40),
price integer
);
INSERT INTO t (make, model, price) VALUES
('Fillmore', 'F_M1', 10000),
('Fillmore', 'F_M2', 20000),
('McLaren', 'M_M2', 40000),
('McLaren', 'M_M2', 60000),
('Toyota', 'T_M1', 12000),
('Toyota', 'T_M2', 24000),
('Toyota', 'T_M3', 48000);
SELECT
make, model, MIN(price)
FROM
t
GROUP BY
make, model
ORDER BY make, model;
Result:
Schema (PostgreSQL v10.0)
CREATE TABLE t (
make varchar(40),
model varchar(40),
price integer
);
INSERT INTO t (make, model, price) VALUES
('Fillmore', 'F_M1', 10000),
('Fillmore', 'F_M2', 20000),
('McLaren', 'M_M2', 40000),
('McLaren', 'M_M2', 60000),
('Toyota', 'T_M1', 12000),
('Toyota', 'T_M2', 24000),
('Toyota', 'T_M3', 48000);
Query #1
SELECT
make, model, MIN(price)
FROM
t
GROUP BY
make, model
ORDER BY make, model;
make
model
min
Fillmore
F_M1
10000
Fillmore
F_M2
20000
McLaren
M_M2
40000
Toyota
T_M1
12000
Toyota
T_M2
24000
Toyota
T_M3
48000
View on DB Fiddle

Related

SQL GROUP BY multiple columns with one non-repeating column

I am attempting to query multiple columns in order to display the heaviest ship for each builder/company name.
When using my above query I instead receive the results for every ships weight instead of the heaviest ship for each builder. I have spent a few hours trying to discern what is needed to cause the builder column to be distinct.
You have to remove 'shipname' from the group by list to get the max weight for each builder, then join the query to the original table to get the ship name as the following:
select T.builder, T.shipname, T.weight
from ship T join
(
select builder, max(weight) mx
from ship
group by builder
) D
on T.builder=D.builder and T.weight=D.mx
order by T.builder
You may also use DESNE_RANK() function to get the required results as the following:
select top 1 with ties
builder, shipname, weight
from ship
order by dense_rank() over (partition by builder order by weight desc)
See a demo on SQL Server (I supposed that you are using SQL Server from the posted image).
You don't need to apply a GROUP BY clause in this situation. It will be sufficient to check whether the ship's weight is the highest weight for the current builder. This can be done with a simple sub query:
SELECT
builder, shipname, weight
FROM
ship
WHERE
weight = (SELECT MAX(i.weight) FROM ship i WHERE i.builder = ship.builder)
ORDER BY builder;
I always think the simplest way to handle a complex query starts with a Common Table Expression (CTE). If you’re new to this, a CTE is a query which is run as a first step, so that you can use its results in the next step.
WITH cte AS (
SELECT builder, max(weight) AS weight
FROM data
GROUP BY builder
)
SELECT *
FROM cte JOIN data ON cte.builder=data.builder AND cte.weight=data.weight;
The CTE above fetches the rows with the maximum weights:
SELECT builder, max(weight) AS weight
FROM data
GROUP BY builder
builder
weight
Ace Shipbuilding Corp
95000
Ajax
90000
Jones
95000
Master
80000
You now have the maximum weight for each builder.
You then join this result with the original data to fetch the rows which match the weights and builders:
builder
weight
builder
shipname
weight
Master
80000
Master
Queen Shiney
80000
Jones
95000
Jones
Princess of Florida
95000
Ajax
90000
Ajax
Prince Al
90000
Ace Shipbuilding Corp
95000
Ace Shipbuilding Corp
Ocean V
95000
Ace Shipbuilding Corp
95000
Ace Shipbuilding Corp
Sea Peace
95000
Note that there is a tie for the Ace Shipbuilding Corp.
The above solution supposes that the data you have in your sample is the original data.

SQL - count function not working correctly

I'm trying to count the blood type for each blood bank I'm using oracle DB
the blood bank table is created like this
CREATE TABLE BloodBank (
BB_ID number(15),
BB_name varchar2(255) not NULL,
B_type varchar2(255),CONSTRAINT
blood_ty_pk FOREIGN KEY
(B_type) references BloodType(B_type),
salary number(15) not Null,
PRIMARY KEY (BB_ID)
);
INSERT INTO BloodBank (BB_ID,BB_name,b_type, salary)
VALUES (370,'new york Blood Bank','A+,A-,B+',12000);
INSERT INTO BloodBank (BB_ID,BB_name,b_type, salary)
VALUES (791,'chicago Blood Bank','B+,AB-,O-',90000);
INSERT INTO BloodBank (BB_ID,BB_name,b_type, salary)
VALUES (246,'los angeles Blood Bank','O+,A-,AB+',4500);
INSERT INTO BloodBank (BB_ID,BB_name,b_type, salary)
VALUES (360,'boston Blood Bank','A+,AB+',13000);
INSERT INTO BloodBank (BB_ID,BB_name,b_type, salary)
VALUES (510,'seattle Blood Bank','AB+,AB-,B+',2300);
select * from BloodBank;
when I use the count function
select count(B_type)
from bloodbank
group by BB_ID;
the result would be like this
so why the count function is not working correctly?
I'm trying to display each blood bank blood type count which is not only one in this case
I hope I don't get downvoted for solving the specific problem you're asking about, but this query would work:
select bb_id,
bb_name,
REGEXP_COUNT(b_type, ',')+1
from bloodbank;
However, this solution ignores a MAJOR issue with your data, which is that you do not normalize it as #Tim Biegeleisen correctly instructs you to do. The solution I've provided is EXTREMELY hacky in that it counts the commas in your string to determine the number of blood types. This is not at all reliable, and you should 100% do what Tim B recommends. But for the circumstances you find yourself in, this will tell you how many different blood types are kept at a specific blood bank.
http://sqlfiddle.com/#!4/8ed1c2/2
You should normalize your data and get each blood type value onto a separate record. That is, your starting data should look like this:
BB_ID | BB_name | b_type | salary
370 | new york Blood Bank | A+ | 12000
370 | new york Blood Bank | A- | 12000
370 | new york Blood Bank | A+ | 12000
... and so on
With this data model, the query you want is something along these lines:
SELECT BB_ID, BB_name, b_type, COUNT(*) AS cnt
FROM bloodbank
GROUP BY BB_ID, BB_name, b_type;
Or, if you want just counts of types across all bloodbanks, then use:
SELECT b_type, COUNT(*) AS cnt
FROM bloodbank
GROUP BY b_type;

Given account contributions: How to sum contributions per individual? In relation to a threshold?

Given contribution amounts per account, how do I 1)SUM the contributions made by each individual, 2)Find the number of people who have contributed <, =, or > $5,000?
Right now I have a database table "[dbo].[FakeRRSPs]" which looks like:
Account_ID
Personal_ID
Contributions
My current code gives the # of unique individuals successfully:
select distinct(personal_id), sum(contributions), count(account_id),
(select count(distinct(personal_id))
from [dbo].[FakeRRSPs]
)
from [dbo].[FakeRRSPs]
where personal_id is not null
group by personal_id
For example, there are 2M people holding 2.5M accounts.
Issues I face:
How do I count the number of individuals who contribute below, at, or
above the $5K threshold (after SUM(contribution) per person)
There are people who contribute $10K total for example, $5K in 2
accounts. Both accounts are picked up when I'm hoping to only capture
the SUM(Contribution) for this person.
I hope this is clear enough - it certainly isn't to me! Thanks everyone.
SQL Fiddle
MS SQL Server 2017 Schema Setup:
create table Contribution (PID int,AID int,C int)
insert into Contribution(PID,AID,C)VALUES(235,1245,1200)
insert into Contribution(PID,AID,C)VALUES(256,1246,0)
insert into Contribution(PID,AID,C)VALUES(256,1247,3500)
insert into Contribution(PID,AID,C)VALUES(256,1248,10000)
insert into Contribution(PID,AID,C)VALUES(421,1249,0)
Query 1:
select * from (select PID,sum(C) AS SC from Contribution
group by PID) as test
where test.SC<=5000
Results:
| PID | SC |
|-----|------|
| 235 | 1200 |
| 421 | 0 |

Access SQL: Combining SELECT and USER INPUT

I'm creating a form in Access where users can select a commodity out of a list of possible commodities, and then a query calculates the average price of the selected commodity.
The input field for the user is a list (List147). Let's say the user selects Copper, then I want the average to be returned for Copper. The prices of all commodities are in a table called CommMaterial. The snip below shows what the table looks like.
I'm fairly new at SQL and am not sure how to code this. It appears as if the SELECT statement needs to be dynamic, but I don't know how to do this. I envision something like this:
SELECT AVG(CommMaterial.[Forms]![NameForm]![List147])
FROM CommMaterial;
To keep from dynamic sql and VBA, You could use some SQL to get your table into a slightly more query-able format:
SELECT DateComm, 'Copper' as Metal, Copper as Price FROM CommMaterial
UNION ALL
SELECT DateComm, 'Nickel' as Metal, Nickel as Price FROM CommMaterial
UNION ALL
SELECT DateComm, 'Aluminum' as Metal, Aluminum as Price FROM CommMaterial;
Which will give you a result set with three columns:
DateComm | Metal | Price
You could save that as a query qry_CommMaterial and then your SQL would be:
SELECT Avg(Price) FROM qry_CommMaterial WHERE metal = [Forms]![NameForm]![List147];
You could also just force it all into one big statement too:
SELECT Avg(Price)
FROM (
SELECT DateComm, 'Copper' as Metal, Copper as Price FROM CommMaterial
UNION ALL
SELECT DateComm, 'Nickel' as Metal, Nickel as Price FROM CommMaterial
UNION ALL
SELECT DateComm, 'Aluminum' as Metal, Aluminum as Price FROM CommMaterial) as subUnion
WHERE metal = [Forms]![NameForm]![List147];

Query to order data while maintaining grouping?

I have a request which I can accomplish in code but am wondering if it is at all possible do do on SQL alone. I have a products table that has a Category column and a Price column. What I want to achieve is all of the products grouped together by Category, and then ordered by the cheapest to most expensive in both the category and all the categories combined. So for example :
Category | Price
--------------|---------------------
Basin | 500
Basin | 700
Basin | 750
Accessories | 550
Accessories | 700
Accessories | 1000
Bath | 700
As you can see the cheapest item is a basin for 500, then an Accessory for 550 then a bath for 700. So I need the categories of products to be sorted by their cheapest item, and then each category itself in turn to be sorted cheapest to most expensive.
I have tried partitioning, grouping sets ( which i know nothing about ) but still no luck so eventually resorted to my strength ( C# ) but would prefer to do it straight in SQL if possible. One last side note : This query is hit quite often so performance is key so if possible i would like to avoid temp tables / cursors etc
I think using MIN() with a window (OVER) makes it clearest what the intent is:
declare #t table (Category varchar(19) not null,Price int not null)
insert into #t (Category,Price) values
('Basin',500),
('Basin',700),
('Basin',750),
('Accessories',550),
('Accessories',700),
('Accessories',1000),
('Bath',700)
;With FindLowest as (
select *,
MIN(Price) OVER (PARTITION BY Category) as Lowest
from
#t
)
select * from FindLowest
order by Lowest,Category,Price
If two categories share the same lowest price, this will still keep the two categories separate and sort them alphabetically.
Select...
Order by category, price desc
SELECT p.category,p.price
FROM products p,(select category,min(price) mn from products group by category order by mn) tab1
WHERE p.category=tab1.category
GROUP BY p.category,p.price,tab1.mn
order by tab1.mn,p.category;
Is this what you want?
I think, you do not need GROUP BY clause in your query. If I got your goal correctly, you can try by substituting actual categories in your ORDER BY clause with the minimum price per category inside the subquery.That will allow you getting all the categories sequential, i.e. not Basin - 500; Accessories - 550, but everything for Basin first. After that, you can group by ordinary Price inside each category.
SELECT *
FROM products p
ORDER BY
(SELECT MIN(Price) FROM products p2 WHERE p2.Category=p.Category
),
Price;