SQL Group by but additional column info is needed - sql

I'm stuck with this query. I wonder if somebody can give me some idea how to resolve this.
Here is my table:
I basically want to group by product with the highest value of quality. But at the same time I also need to grab completed column.
select
Product, max(Quality) as Quality
from
[Table]
group by
Product
When I group it, I cannot retrieve completed column.
Any other method to have same result above with completed column? in this case 1, 1 will be displayed.
Thanks in advance

You can take the output of this query and inner join it with the original table...
select t1.*
from table_name t1
inner join
(select product, max(quality) as maxquality
from table_name
group by product) t2 on t1.product = t2.product
and t1.quality = t2.maxquality

In SQL Server, you can only SELECT columns that are part of the GROUP BY clause, or aggregate functions on any of the other columns.
You can either try adding it to the GROUP BY clause:
SELECT Product,
Completed,
max(Quality) AS Quality,
FROM [Table]
GROUP BY Product, Completed
Or use an aggregate function:
SELECT Product,
max(Completed),
max(Quality) AS Quality,
FROM [Table]
GROUP BY Product

Related

How do we find frequency of one column based off two other columns in SQL?

I'm relatively new to working with SQL and wasn't able to find any past threads to solve my question. I have three columns in a table, columns being name, customer, and location. I'd like to add an additional column determining which location is most frequent, based off name and customer (first two columns).
I have included a photo of an example where name-Jane customer-BEC in my created column would be "Texas" as that has 2 occurrences as opposed to one for California. Would there be anyway to implement this?
If you want 'Texas' on all four rows:
select t.Name, t.Customer, t.Location,
(select t2.location
from table1 t2
where t2.name = t.name
group by name, location
order by count(*) desc
fetch first 1 row only
) as most_frequent_location
from table1 t ;
You can also do this with analytic functions:
select t.Name, t.Customer, t.Location,
max(location) keep (dense_rank first order by location_count desc) over (partition by name) most_frequent_location
from (select t.*,
count(*) over (partition by name, customer, location) as location_count
from table1 t
) t;
Here is a db<>fiddle.
Both of these version put 'Texas' in all four rows. However, each can be tweaks with minimal effort to put 'California' in the row for ARC.
In Oracle, you can use aggregate function stats_mode() to compute the most occuring value in a group.
Unfortunately it is not implemented as a window function. So one option uses an aggregate subquery, and then a join with the original table:
select t.*, s.top_location
from mytable t
inner join (
select name, customer, stats_mode(location) top_location
from mytable
group by name, customer
) s where s.name = t.name and s.customer = t.customer
You could also use a correlated subquery:
select
t.*,
(
select stats_mode(t1.location)
from mytable t1
where t1.name = t.name and t1.customer = t.customer
) top_location
from mytable t
This is more a question about understanding the concepts of a relational database. If you want that information, you would not put that in an additional column. It is calculated data over multiple columns - why would you store that in the table itself ? It is complex to code and it would also be very expensive for the database (imagine all the rows you have to calculate that value for if someone inserted a million rows)
Instead you can do one of the following
Calculate it at runtime, as shown in the other answers
if you want to make it more persisent, you could embed that query above in a view
if you want to physically store the info, you could use a materialized view
Plenty of documentation on those 3 options in the official oracle documentation
Your first step is to construct a query that determines the most frequent location, which is as simple as:
select Name, Customer, Location, count(*)
from table1
group by Name, Customer, Location
This isn't immediately useful, but the logic can be used in row_number(), which gives you a unique id for each row returned. In the query below, I'm ordering by count(*) in descending order so that the most frequent occurrence has the value 1.
Note that row_number() returns '1' to only one row.
So, now we have
select Name, Customer, Location, row_number() over (partition by Name, Customer order by count(*) desc) freq_name_cust
from table1 tb_
group by Name, Customer, Location
The final step puts it all together:
select tab.*, tb_.Location most_freq_location
from table1 tab
inner join
(select Name, Customer, Location, row_number() over (partition by Name, Customer order by count(*) desc) freq_name_cust
from table1
group by Name, Customer, Location) tb_
on tb_.Name = tab.Name
and tb_.Customer = tab.Customer
and freq_name_cust = 1
You can see how it all works in this Fiddle where I deliberately inserted rows with the same frequency for California and Texas for one of the customers for illustration purposes.

MIN and DISTINCT for ORACLE DB

I have this query
SELECT table_article.articleID, table_article_to_date.sellDate, MIN(table_article.price) AS minPrice
FROM table_article table_article
LEFT JOIN table_article_to_date table_article_to_date ON (table_article.ord_no=table_article_to_date.ord_no)
WHERE table_article.price > 0 AND table_article_to_date.sellDate BETWEEN_TWO_DATES
GROUP BY table_article.articleID, table_article_to_date.sellDate, table_article.price
For the sell_date I use a time range. My problem is, that i get more than one entrie each articleID.
I wish to have the lowest price of each articleID in a specified time range. DISTINCT is not woking with MIN
Givs a change to make this with a query?
The problem here is that you are adding the sell date to the GROUP BY clause. TO solve the issue, you need to take it out and make use of subqueries to get it back. To achieve this, you can do an inner join of the query and a query with the id, sell date and prize on the id and prize.
Also, no need for the prize in the group by, since it's already in the MIN.
SELECT articleData.articleId, articleData.sellDate, articleData.price FROM
(
SELECT articleId, MIN(price)
FROM table
[...]
GROUP BY articleId
) AS minPrice
INNER JOIN
(
SELECT articleId, sellDate, price
FROM table
) AS articleData
ON minPrice.price = articleData.price AND minPrice.articleId = articleData.articleId

Table inner join itself

I have a table with 3 columns (code, state, date), it records the history of a code state, each code may have changed state multiple times.
I want to show the last state of each code what I did was like this
SELECT code,MAX(date), ....
FROM table
GROUP BY code.
I don't know what to put exactly to get the state. I tried to just put state so it gets the state corresponding to the combination of code,max(date) but it gives me the error of not in aggregate function.
thank you in advance for your help.
If I understand you have data such as
CODE State Date
1 IL 1/1/2016
1 IA 1/1/2017
1 AL 1/1/2015
and you want to see in your results
1 IA 1/1/2017
using a window function and a common table expression (with): we assign a row number to each code based on the date in descending order and return only the first row for each.
With CTE AS (SELECT code
, date
, state
, Row_number() over (partition by code order by date desc) RN
FROM table )
SELECT Code, Date, State
FROM CTE
WHERE RN =1
Using a subquery: (we get the max date for each code and then join back to the base set to limit the rows returned.
SELECT A.code, A.date, A.state
FROM table A
INNER JOIN (SELECT max(date) mdate, code
FROM table
GROUP BY code) B
on A.Code = B.Code
and A.Date = B.MDate
The later query was used when/if window functions are not available. The modern method of solving your question is using the first approach.
In essence what the 1st query does is assign the # 1 to x for each code based on the date descending. So the max date gets a RN of 1 for each code. Thus when we say where RN = 1 we only return codes/states/records having max dates for the code in question. We use a with statement because we need the RN to materialize (actually get generated in memory) so that we can then limit by it in the second part of the with (common table expression) query.
If you're doing an aggregate, like MAX(), then all other non-aggregate columns that are in your select, need to also be in your GROUP BY. That's why you're getting the error when you add state to only the select. If you add it to the select and group by it, you'll get your results:
SELECT State, Code, MAX(Date)
FROM table
GROUP BY State, Code
If you want to user inner join like you mention in your post Inner join back to itself with matching code and date
SELECT *
FROM table t1
INNER JOIN (SELECT code,MAX(date)
FROM table
GROUP BY code) codeWithLatestDate ON t1.code = codeWithLatestDate.code AND t1.date = codeWithLatestDate.dat3
However I would suggest add state to your GROUP BY clause and SELECT cluase
SELECT code,MAX(date),state
FROM table
GROUP BY code, state
Youn can do it with a join to itself
SELECT State,Code,Date
FROM table t
JOIN (
SELECT Code, MAX(Date) as Date
FROM table
GROUP BY Code) t1 on t1.Code= t.Code and t.Date=t1.Date

Get the first instance of a row using MS Access

EDITED:
I have this query wherein I want to SELECT the first instance of a record from the table petTable.
SELECT id,
pet_ID,
FIRST(petName),
First(Description)
FROM petTable
GROUP BY pet_ID;
The problem is I have huge number of records and this query is too slow. I discovered that GROUP BY slows down the query. Do you have any idea that could make this query faster? or better, a query wherein I don't need to use GROUP BY?
"The problem is I have huge number of records and this query is too slow. I discovered that GROUP BY slows down the query. Do you have any idea that could make this query faster?"
And an index on pet_ID, then create and test this query:
SELECT pet_ID, Min(id) AS MinOfid
FROM petTable
GROUP BY pet_ID;
Once you have that query working, you can join it back to the original table --- then it will select only the original rows which match based on id and you can retrieve the other fields you want from those matching rows.
SELECT pt.id, pt.pet_ID, pt.petName, pt.Description
FROM
petTable AS pt
INNER JOIN
(
SELECT pet_ID, Min(id) AS MinOfid
FROM petTable
GROUP BY pet_ID
) AS sub
ON pt.id = sub.MinOfid;
Your Query could change as,
SELECT ID, pet_ID, petName, Description
FROM petTable
WHERE ID IN
(SELECT Min(ID) As MinID FROM petTable GROUP BY pet_ID);
Or use the TOP clause,
SELECT petTable.petID, petTable.petName, petTable.[description]
FROM petTable
WHERE petTable.ID IN
(SELECT TOP 1 ID
FROM petTable AS tmpTbl
WHERE tmpTbl.petID = petTable.petID
ORDER BY tmpTbl.petID DESC)
ORDER BY petTable.petID, petTable.petName, petTable.[description];

How can I use the GROUP BY SQL clause with no aggregate function?

When I try to use the following SELECT statement:
SELECT [lots of columns]
FROM Client, Customer, Document, Group
WHERE [some conditions]
GROUP BY Group.id
SQL Server complains that the columns I selected are not part of the GROUP BY statement nor an aggregate function. Am I using GROUP BY wrong? What should I be using instead?
To return all single occurences of a group by field, together with associated field values, write a query like:
select group_field,
max(other_field1),
max(other_field2),
...
from mytable1
join mytable2 on ...
group by group_field
having count(*) = 1;
Yes, you are using GROUP BY incorrectly. The point of using GROUP BY is to use aggregate functions. If you have no aggregrate functions you probably want SELECT DISTINCT instead.
SELECT DISTINCT
col1,
col2,
-- etc
coln
FROM Client
JOIN Customer ON ...
JOIN Document ON ...
JOIN [Group] ON ...
WHERE ...
My first guess would be that the problem is that you have table called Group, which I believe is a reserved word in SQL. Try wrapping the Group name with ' '
You want to group by all columns you are selecting that is not in an aggregate funcion.
SELECT ProductName, ProductCategory, SUM(ProductAmount)
FROM Products
GROUP BY ProductName, ProductCategory
This will give you a disticnt result of Product names and categories with the sum total of product amount in all aggregate child records for that group.