I want to load best seller products by quantity. These are my tables:
Product
id name
1 AA
2 BB
Productorder
order_id product_id quantity
1 1 10
2 1 100
3 2 15
4 1 15
This is my Spring Data Repository:
#Repository
public interface ProductRepository extends JpaRepository<Product, Long> {
#Query(value = "select top 5 p.name, sum(po.quantity) as total_quantity from product p " +
"inner join productorder po " +
"on p.id = po.product_id " +
"group by p.id, p.name " +
"order by total_quantity desc", nativeQuery = true)
List<Product> findTopFiveBestSeller();
}
I am getting HsqlException: Column not found: id
I think this error does not have anything to do with id column, as it is there for both tables. Do "group by Sum queries" work with Spring data? Because it seems little strange for me as Spring Data should select just product attributes from the database, and with this sql we are selecting also the sum(po.quantity). Can Spring data handle this and convert the result as a List?
PS: I am using HSQLDB embedded as DB.
After changing the select statements projection from p.name to p.* to indicate that I am selecting multiple values rather than just String objects that have to be magically converted to Product objects, this works:
#Repository
public interface ProductRepository extends JpaRepository<Product, Long> {
#Query(value = "select top 5 p.*, sum(po.quantity) as total_quantity from product p " +
"inner join productorder po " +
"on p.id = po.product_id " +
"group by p.id, p.name " +
"order by total_quantity desc", nativeQuery = true)
List<Product> findTopFiveBestSeller();
}
Thanks #JMK und #JB Nizet.
Related
I have jpql query which maps output of query to specific object
return em.createQuery(
"SELECT DISTINCT new org.some.great.project.Statuses(u.accountStatus, COUNT(u.accountStatus)) "
+ "FROM XUser u LEFT JOIN u.products p "
+ "WHERE p.id IN :productIds "
+ "GROUP BY u.accountStatus", Statuses.class)
.setParameter(PRODUCT_IDS, productIds)
.getResultList();
And everything is ok, until XUser does have only on relation to Product table.
If it has more than one, count(xx) will add additional record.
My mapping in XUser looks like following
public class XUser {
//other
#ManyToMany
#JoinTable(
name = "XUser_product",
joinColumns = #JoinColumn(
name = "user_id",
referencedColumnName = "id"
),
inverseJoinColumns = #JoinColumn(
name = "product_id",
referencedColumnName = "id"
)
)
private Set<Product> products;
//other
}
If I check logs hibernate (maps jpql to followin sql) is running query:
SELECT DISTINCT t.ACCOUNTSTATUS, COUNT(t.ACCOUNTSTATUS) FROM XUser x, XUSER_PRODUCT xp , PRODUCT p
WHERE x.ID = xp.XUSER_ID
AND xp.PRODUCT_ID = p.ID
AND p.ID IN (some nubers)
GROUP BY t.ACCOUNTSTATUS;
So if I modify it a little, and instead of returning count with status, I want to return everything I am getting
ID
NAME
ACCOUNTSTATUS
XUSER_ID
PRODUCT_ID
ID
NM
PRICE
1
Peter
DEAD
1
12
12
test
199
1
Peter
DEAD
1
123
123
fruit
999
2
Tom
OK
2
98
98
tabasco
11
3
Kate
DEAD
3
87
87
mars
2
So as you can see I have displayed Peter with ID=1 two times. And If this query is changed to previous one I would see
DEAD: 3 // and I need to have here 2
OK : 1
Any ideas, hints how to handle it?
I am writing an #Query for a repository method. This query should return all Movie which have an average Rating less than the given parameters value:
#Query(value =
"select m from Movie m " +
"where (select avg(r.rating) from Review r " +
"where m.id = r.movie.id) < :rating")
List<Movie> findMoviesWithAverageRatingLowerThan(#Param("rating") Double rating);
The problem is that I have a requirement that when a Movie has no ratings, it should be interpreted as an average rating equal 0.
I have a clue that I should use switch/case combined with when, but as for now I have no idea how to do it.
Is this even possible to achieve this in one query?
You can try to use COALESCE expression like below:
#Query(value =
"select m from Movie m " +
"where (select avg(COALESCE(r.rating, 0)) from Review r " +
"where m.id = r.movie.id) < :rating")
List<Movie> findMoviesWithAverageRatingLowerThan(#Param("rating") Double rating);
I have 2 tables stock and stockprice. I want to get all stocks with their last updated price using Entity Framework 6.
I first select * from stock, then use foreach to get last updated price for each symbol. How can do that with just one query using entity framework 6?
Stock:
Id Symbol
-------------
1 AAPL
2 MSFT
Stockprice:
Id Symbol Price LastUpdatedDate
-------------------------------------
1 AAPL 120 11/7/2016
2 AAPL 121 11/2/2016
3 AAPL 123 11/3/2016
4 MSFT 111 11/2/2016
5 MSFT 101 11/6/2016
I want to get last updated price for each symbol.
AAPL 120 11/7/2016
MSFT 101 11/6/2016
if you have list of object with property
Id,Symbol,Price,LastUpdatedDate
let say
List<ItemPrice> itemPrice
then you can do like this
var lastUpdated= from item in itemPrice
group item by item.Symbol into itemGroup
let maxDate = itemGroup.Max(gt => gt.LastUpdatedDate)
select new RegistrationModel
{
Symbol = itemGroup.Key,
LastUpdatedDate= maxDate,
Price= itemGroup.First(gt2 => gt2.ExpiryDateAD == maxDate).Price
}).ToList()
i hope it may help you
Try this
using(var db= new DbContext())
{
//This will fetch all your stock and include all corresponding StockPrices.
var stockWithPrices = db.Stock
.Select(x=> new
{
Name = x. Name
Price = x.StockPrices.OrderByDescending(y=>y.LastUpdatedDate).FirstOrDefault().Price
Date = x.StockPrices.OrderByDescending(y=>y.LastUpdatedDate).FirstOrDefault().Date
}.ToList();
}
Assuming your model look like this
public partial class Stock
{
public int Id {get;set;}
public string Symbol {get;set;}
public ICollection<StockPrice> StockPrices {get;set;}
}
Note: I realize you prefer a pure EF solution but this is another way to do it and I think it will be more scalable and supportable.
Query :
Select A.Symbol, C.Price, A.MaxDate
(
Select Max(LastUpdatedDate) as MaxDate, Symbol
FROM stockprice A INNER JOIN stock B on A.Symbol = B.Symbol
Group By Symbol
) A
INNER JOIN StockPrice C on A.Symbol = C.Symbol AND A.MaxDate = C.LastUpdatedDate
To make a View :
Create View vStockPrice AS
Select A.Symbol, C.Price, A.MaxDate
(
Select Max(LastUpdatedDate) as MaxDate, Symbol
FROM stockprice A INNER JOIN stock B on A.Symbol = B.Symbol
Group By Symbol
) A
INNER JOIN StockPrice C on A.Symbol = C.Symbol AND A.MaxDate = C.LastUpdatedDate
Go
To Do it using Stored Procedures:
Create Procedure usp_StockPrice AS
Begin
Select A.Symbol, C.Price, A.MaxDate
(
Select Max(LastUpdatedDate) as MaxDate, Symbol
FROM stockprice A INNER JOIN stock B on A.Symbol = B.Symbol
Group By Symbol
) A
INNER JOIN StockPrice C on A.Symbol = C.Symbol AND A.MaxDate = C.LastUpdatedDate
END
Then you simply query the view like you would query any table using EF or use the process to call a stored procedure.
var query = from rows in vStockPrice select ..... ;
The advantage of doing it in View or SP is that you can handle any future changes to your requirements by simply altering the base query. (Say you want to ignore penny stocks so all it will take is a where clause : where price > 1.0 ). You will not have to recompile your C# code (and your DBA will like this approach for other reasons !!)
Let me know if you want me to expand (or you think this is not relevant )
I have 3 tables of data that I need to join to make one table and show the following fields:
u.master_id, p.surname, p.forename1, c.number, u.status
Here are the table details:
Table 1 (person p)
Field name = entity_id (number)
Field name = surname(text)
Field name = forename1(text)
Field name = auditflag
Table 2 (users u)
Field name = master_id (number)
Field name = email (number)
Field name = status (text)
Field name = auditflag
Table 3 (contact c)
Field name = master_id (number)
Field name = entity_id (number)
Field name = number (text = holds the actual email address)
Field name = auditflag
The master_id field from table 2 and 3 links to the person entity_id ( master_id=p.entity_id )
The email field from table 2 links to the entity_id in table 3 ( u.email=c.entity_id ) to get the number
The auditflag is used to mark each as an active record i.e. ( p.auditflag=1 AND u.auditflag=1 AND c.auditflag=1 )
I'm using Excel 2010 VBA - joining to a PostGreSQL database to get the data.
Below is the code I am using to join the 2 tables which works fine, but my problem is that "email" only shows a number because the actual address is in a 3rd table (Contact) under the field called "number".
I don't know how to include the 3rd table to show the field c.number in my query.
Const sqlconnection = "Provider=oledb;"
Dim conn As New Connection
conn.ConnectionString = sqlconnection
conn.Open
Dim rs As Recordset
Dim prs As String
prs = "SELECT u.master_id, p.surname, p.forename1, u.email, u.status " _
& "FROM users u INNER JOIN person p ON u.master_id=p.entity_id " _
& "WHERE u.auditflag=1 AND p.auditflag=1 " _
& "ORDER BY u.master_id "
Set rs = conn.Execute(prs)
With ActiveSheet.QueryTables.Add(Connection:=rs, Destination:=Range("A1"))
.Refresh
End With
You have all the pieces; all you need to do is add the third table to the query and select the columns you want.
Dim prs As String
prs = "SELECT u.master_id, p.surname, p.forename1, c.number as email, u.status " _
& "FROM users u INNER JOIN person p ON u.master_id=p.entity_id " _
& " INNER JOIN contact c ON c.master_id=p.entity_id and u.email=c.entity_id " _
& "WHERE u.auditflag=1 AND p.auditflag=1 AND c.auditflag=1 " _
& "ORDER BY u.master_id "
As written, this query will only get users that have active (auditflag=1) person rows and active contact rows. Changing to left joins and moving the auditflag checks to the join condition would allow returning users without active rows in all three tables.
As a side note, I find the column names in your tables are actively working against easily understanding what they mean; if you can, I'd suggest re-thinking the names you're using.
I am trying to generate a report on a sql server database in asp.net and I am getting the results of some columns as a product of two columns. Here is the code
comm.CommandText = "SELECT Count(Courses.CourseID) AS CourseCount, Count(Students.StudentID) AS StudentCount, Schools.Name, Schools.StartDate, Schools.SchoolFees " +
"FROM Schools" +
"LEFT JOIN Courses ON (Schools.SchoolID = Courses.SchoolID)" +
"LEFT JOIN Students ON (Schools.SchoolID = Students.SchoolID) " +
"WHERE Schools.Active = 1 " +
"GROUP BY Schools.Name, Schools.StartDate, Schools.SchoolFees";
When I run the code, the result displays, but the columns for "CourseCount" and "StudentCount" display a value that is a product of each individual column. "CourseCount" is normally 288 and "StudentCount" is 38 but when I run the code, both "CourseCount" and "StudentCount" display 10944 which is 38 x 288.
Anyway I can make them display the correct values?
Changing your code from using a count of all rows, to a count of distinct values only, should return the results you expect
comm.CommandText = "SELECT Count(DISTINCT Courses.CourseID) AS CourseCount, Count(DISTINCT Students.StudentID) AS StudentCount, Schools.Name, Schools.StartDate, Schools.SchoolFees " +
"FROM Schools" +
"LEFT JOIN Courses ON (Schools.SchoolID = Courses.SchoolID)" +
"LEFT JOIN Students ON (Schools.SchoolID = Students.SchoolID) " +
"WHERE Schools.Active = 1 " +
"GROUP BY Schools.Name, Schools.StartDate, Schools.SchoolFees";
The results being returned are technically correct, if all schools have courses, and all courses have students
As stated above, it is how you are using the COUNT (), You are asking it to count all, which is why it returns so many. Use count on just the two values you want counted.
This might perform better than the DISTINCT Count method..
comm.CommandText =
"SELECT cc.CourseCount, sc.StudentCount, Schools.Name, Schools.StartDate, Schools.SchoolFees " +
"FROM Schools" +
"OUTER APPLY (SELECT Count(Students.StudentID) StudentCount FROM Students WHERE Students.SchoolID = Schools.SchoolID) sc " +
"OUTER APPLY (SELECT Count(Courses.CourseID) CourseCount FROM Courses WHERE Courses.SchoolID = Schools.SchoolID) cc " +
"WHERE Schools.Active = 1 ";