I'm trying to create a simple query that will find a person with highest average marks and display some basic information about them. It's retrieving the proper record, but I can't make MySQL display students.classId field. The error I'm getting in LibreOffice Base is
Not in aggregate function or group by clause.
Query with error:
SELECT CONCAT(`students`.`surname`, CONCAT(' ', `students`.`name`)) AS `Student`,
AVG(CAST(`marks`.`mark` AS DECIMAL (10, 2))) AS `Average`,
`students`.`classId`
FROM `students`, `marks`, `subjects`
WHERE `marks`.`subjectId` = `subjects`.`subjectId`
AND `students`.`studentId` = `marks`.`markId`
GROUP BY `students`.`surname`, `students`.`name`
ORDER BY `Average` DESC LIMIT 1;
Query without error:
SELECT CONCAT(`students`.`surname`, CONCAT(' ', `students`.`name`)) AS `Student`,
AVG(CAST(`marks`.`mark` AS DECIMAL (10, 2))) AS `Average`
FROM `students`, `marks`, `subjects`
WHERE `marks`.`subjectId` = `subjects`.`subjectId`
AND `students`.`studentId` = `marks`.`markId`
GROUP BY `students`.`surname`, `students`.`name`
ORDER BY `Average` DESC LIMIT 1;
I'm not really experienced with SQL, but I think that posting table definitions isn't necessary in this case. If I am wrong, please leave a note in the comments, I'll update the question as soon as possible.
Please note that it is not a homework.
The problematic item is this:
`students`.`classId`
Since the GROUP BY query produces a single row for one or more rows of the joined tables, that single row may correspond to more than one students.classId value.
That is what SQL is asking you to fix: it wants to know which of potentially many items of students.classId you want it to return. The two choices are adding an aggregate function, say
MIN(`students`.`classId`) AS StudentClassId
or using students.classId in the GROUP BY clause:
GROUP BY `students`.`surname`, `students`.`name`, `students`.`classId`
Note that if you go with the later choice, the aggregation would be per student / class pair, not per student.
Related
The question is about using a column (which is created in query) as where clause criteria.
There are 2 tables named transactions and transactionmovements.
In transaction, there are unique info for transaction like date, counterparty etc.
In transactionmovements, there are articles which is used in transaction. Such as product, quantity, price, etc. And transactionmovements has a 'transaction' column which references to transactions.id shows which transaction the movement belongs.
In the query, I created a totalPrice value with sum of quantity*price of each movement that belongs to a transaction.
Everything works perfectly but the last parameter of WHERE clause. If I delete "AND
totalPrice > 10" part, it gives me everything including totalPrice and totalQuantity of a transaction.
But if I place "AND totalPrice > 10" to the end, it returns following error:
-#1054 - Unknown column 'totalPrice' in 'where clause'
SELECT
`transactions`.id,
`transactions`.type,
`transactions`.date,
`transactions`.VAT,
`transactions`.currency,
`companies`.name AS counterparty,
COALESCE
(sum(`transactionmovements`.price*`transactionmovements`.quantity)
+(`transactions`.shippingQuantity*`transactions`.shippingPrice)) as totalPrice,
COALESCE
(sum(`transactionmovements`.quantity)) as totalQuantity
FROM
`transactions`
LEFT JOIN `companies` ON `transactions`.counterparty = `companies`.id
LEFT JOIN `transactionmovements` ON `transactions`.id=`transactionmovements`.transaction
WHERE ( `transactions`.type = 'p' OR `transactions`.type = 'r' OR `transactions`.type = 's' OR `transactions`.type = 't')
AND
(`transactions`.date BETWEEN IFNULL('','1900-01-01') AND IFNULL('2020-02-14',NOW()))
AND
totalPrice > 10
GROUP BY `transactions`.id
ORDER BY id desc
LIMIT 10
I tried using the whole math operation in WHERE clause, but no gains. I tried to use HAVING with WHERE but couldn't manage it.
The last solution I have is running it without filtering by totalPrice and store it into a php array. Then filter in array, but there I can't use LIMIT so array will be very big.
As per how SQL query are executed in the order that dictate SELECT is executed after WHERE, you can't use ALIAS in WHERE since it is unknown at the moment. Therefor, you should change every alias uses outside of SELECT to its definition.
As Thomas Jeriko suggested in comments, creating a view was exactly what I need.
First I create a virtual table with Create View.
CREATE VIEW transactionreportview
AS SELECT
transactions.id id,
transactions.type type,
transactions.date date,
transactions.VAT VAT,
transactions.currency currency,
transactions.counterparty counterparty,
(SUM(transactionmovements.price*transactionmovements.quantity)+transactions.shippingQuantity*transactions.shippingPrice) totalPrice,
SUM(transactionmovements.quantity) totalQuantity
FROM transactions transactions, transactionmovements transactionmovements
WHERE transactions.id = transactionmovements.transaction
GROUP BY transactions.id;
While the virtual table acts like a simple mysql table, I ran a new query in the table
SELECT * FROM transactionReportView WHERE ..."
Then after finishing my work with the virtual table, drop it
DROP VIEW transactionreportview
I have been trying to do this in many ways suggested.
Note: we do not want aggregate function or Partition since this is just a small part of whole Stored procedure and this is client requirement to not have it, so not in option and not possible duplicate of other existing answers / questions
I have a messages table, which has a column from and to, a foreign key to the user table, basically which user sends to whom at simplest. I also have other columns which are isSnoozed and snoozeAt for if the message is snoozed.
So the ordering is according to case. If messages is snoozed then consider snoozeAt time to Order or if not then consider sendAt. (right now we can ignore this condition while ordering, But I mentioned this since we cannot take simply MAX(id) )
I need to get recent most message from messages group by from user id
messages table like :
id -- to -- from -- isSnoozed -- snoozedAt -- sendAt ...
What I tried :
select * from ( select * from messages order by sendAt DESC) as TEMP GROUP BY TEMP.from
I tried many similar approaches but none worked.
I wasted many paid hours but can't find an approach which meets my exact requirement
NOTE: Please ignore typo in query if any, since I cant type in exact query table and names, So i typed in directly here
I figured this out by doing something like this, which could be explained in a simplified way:
select * from message where message.id in (
select
( select id from message where message.from = user.id order by CASE isSnoozed WHEN 0 THEN sendAt ELSE snoozeAt END DESC limit 1) as id
from user where user.id in ( select friends.`whoIsAdded` from friends where friends.`whoAdded` = myId)
) order by CASE isSnoozed WHEN 0 THEN sendAt ELSE snoozeAt END DESC
If I understand correctly, you just want the largest value in one of two columns. Assuming the values are never NULL, you can use greatest():
select m.*
from messages m
where greatest(m.sendAt, m.snoozedAt) =
(select max(greatest(m2.sendAt, m2.snoozedAt))
from messages m2
where m2.from = m.from
);
If the columns can be NULL, then you can use coalesce() to give them more reasonable values.
I'm stucked in a MySQL problem that I was not able to find a solution yet. I have the following query that brings to me the month-year and the number new users of each period in my platform:
select
u.period ,
u.count_new as new_users
from
(select DATE_FORMAT(u.registration_date,'%Y-%m') as period, count(distinct u.id) as count_new from users u group by DATE_FORMAT(u.registration_date,'%Y-%m')) u
order by period desc;
The result is the table:
period,new_users
2016-10,103699
2016-09,149001
2016-08,169841
2016-07,150672
2016-06,148920
2016-05,160206
2016-04,147715
2016-03,173394
2016-02,157743
2016-01,173013
So, I need to calculate for each month-year the difference between the period and the last month-year. I need a result table like this:
period,new_users
2016-10,calculate(103699 - 149001)
2016-09,calculate(149001- 169841)
2016-08,calculate(169841- 150672)
2016-07,So on...
2016-06,...
2016-05,...
2016-04,...
2016-03,...
2016-02,...
2016-01,...
Any ideas: =/
Thankss
You should be able to use a similar approach as I posted in another S/O question. You are on a good track to start. You have your inner query get the counts and have it ordered in the final direction you need. By using inline mysql variables, you can have a holding column of the previous record's value, then use that as computation base for the next result, then set the variable to the new balance to be used for each subsequent cycle.
The JOIN to the SqlVars alias does not have any "ON" condition as the SqlVars would only return a single row anyhow and would not result in any Cartesian product.
select
u.period,
if( #prevCount = -1, 0, u.count_new - #prevCount ) as new_users,
#prevCount := new_users as HoldColumnForNextCycle
from
( select
DATE_FORMAT(u.registration_date,'%Y-%m') as period,
count(distinct u.id) as count_new
from
users u
group by
DATE_FORMAT(u.registration_date,'%Y-%m') ) u
JOIN ( select #prevCount := -1 ) as SqlVars
order by
u.period desc;
You may have to play with it a little as there is no "starting" point in counts, so the first entry in either sorted direction may look strange. I am starting the "#prevCount" variable as -1. So the first record processed gets a new user count of 0 into the "new_users" column. THEN, whatever was the distinct new user count was for the record, I then assign back to the #prevCount as the basis for all subsequent records being processed. yes, it is an extra column in the result set that can be ignored, but is needed. Again, it is just a per-line place-holder and you can see in the result query how it gets its value as each line progresses...
I would create a temp table with two columns and then fill it using a cursor that
does something like this (don't remember the exact syntax - so this is just a pseudo-code):
#val = CURSOR.col2 - (select col2 from OriginalTable t2 where (t2.Period = (CURSOR.Period-1) )))
INSERT tmpTable (Period, NewUsers) Values ( CURSOR.Period, #val)
I need some assistance with my extract. Below is a view of my data and how it is extract from a MS SQL database.
My challenge is that the database does not differentiate from the different "email address" . How do I link email address record to the record above.
Secid|Name|Question|Answer|
2|load1|Name of Principle|Joe Make|
2|load1|Contact Number|12234423|
2|load1|Email address|joemake#mymail.com|
2|load1|Name of Principle|Amy Soup|
2|load1|Contact Number of Principle|23134|
2|load1|Email address|amysoup#mymail.com|
2|load1|Name of Teacher|james blue|
2|load1|Contact Number|8787878|
2|load1|Email Address|jamesblue#mymail.com|
2|load1|Name of Secretary|CHARLES black|
2|load1|Contact Number|989897|
2|load1|Email Address|chblack#mymail.com|
If you don't have any column to order by (e.g. a monotonically increasing identity column, or a timestamp), I'm afraid you're honestly out of luck. There is no way to guarantee any sort of ordering of the rows for any query.
What you can do, however, is export the data into an Excel sheet and then look at it manually and put the rows in the right order, assuming you can figure it out. Unfortunately this is really going to be the only way.
If you had a column you could order by, you can use a join to group the rows, assuming you had a way of identifying the start of each set - in your case a Question like 'Name of %' should probably work. Assuming an identity column called Id, something like:
select t.*, tGroupStart.Id as GroupId
from myTable t
join myTable tGroupStart on tGroupStart.Id <= t.Id
and tGroupStart.Question like 'Name of %'
where not exists (
select 1
from myTable t2
where t2.Id <= t.Id
and t2.Question like 'Name of %'
and t2.Id > tGroupStart.Id
)
I would like to count the total number of rows returned by the following query:
SELECT table1.*, COUNT(table2.fk) * (100/18) AS 'number'
FROM table1 INNER JOIN table2 ON table1.pk = table2.fk
WHERE table1.Street LIKE '$Street%'
AND table1.City LIKE '$City%'
AND table1.Zip LIKE '%$Zip'
AND table1.DOBY LIKE '%$DOBY'
AND table1.DOBM LIKE '%$DOBM'
AND table1.DOBD LIKE '%$DOBD'
AND table1.Gender LIKE '$gender%'
AND table2.year>= 2004
AND table2.type IN ('AA', 'AB', 'AC')
GROUP BY table2.fk
HAVING (COUNT(table2.fk) * (100/18)) >= '$activity'
ORDER BY DOBY, DOBM, DOBD ASC
The query counts the number or times the primary key of table1 occurs as the foreign key of table2, and calculates a percentage ('number') based on a fixed amount. It works well enough, but I'm having trouble getting the total amount of records found for my pagination script.
I would appreciate it if anyone can offer some suggestions or solutions.
u can do SQL_CALC_FOUND_ROWS (google for exact syntax)
And then use SELECT FOUND_ROWS() AS total
Going with what Itay Moav says, a programming language should have a function for the found_rows function. As per the function documentation, it returns the number of rows of a SELECT statement with a LIMIT keyword if the LIMIT keyword wasn't there.
If it doesn't, you can just make another SELECT query to the database: SELECT FOUND_ROWS();. It will return the same information.