How to reuse the part of query - sql

I have Java method which call Native SQL (Oracle) via Hibernate:
public List<Location> getLocationsAround(double latitude, double longitude, double radius, long retailerId) {
List<Location> locationList = (List<Location>) SessionManager.getSession().createSQLQuery(
"SELECT loc.*, distance(ci.coord1, ci.coord2, :latitude, :longitude) as dist " +
"FROM location loc " +
"join rl_retailer_location rrl on rrl.location_id = loc.location_id " +
"join contact_info ci on ci.contact_info_id=loc.contact_info_id " +
"WHERE rrl.retailer_id=:retailerId " +
"and NVL(distance(ci.coord1, ci.coord2, :latitude, :longitude), :limit) <= :radius " +
"ORDER BY dist ASC"
).addEntity("loc", DAO.getInstance().getMappedClass(Location.class))
.setLong("retailerId", retailerId)
.setDouble("latitude", latitude)
.setDouble("longitude", longitude)
.setDouble("radius", radius)
.setDouble("limit", radius + 1.)
.list();
return locationList;
}
For dist calculation is used FUNCTION (stored procedure) distance which has 4 parameters (numbers): latitude1, longitude1, latitude2, longitude2 and returns NUMBER(18,6) or null (if parameter invalid). dist is used in ORDER BY for result set sorting.
This version works as expected.
Question: How can I rewrite query for reuse part dist in WHERE?
Goal: eliminate 2x calculation distance(ci.coord1, ci.coord2, :latitude, :longitude)

In Oracle you can use a query as table in your FROM clause, so you can do something like:
SELECT *
FROM (
SELECT loc.*
, distance(ci.coord1, ci.coord2, :latitude, :longitude) as dist
FROM location loc
JOIN rl_retailer_location rrl on rrl.location_id = loc.location_id
JOIN contact_info ci on ci.contact_info_id=loc.contact_info_id
WHERE rrl.retailer_id=:retailerId
) loc
WHERE dist <= :radius
ORDER BY dist ASC

Related

JPA Query not working for latest record fetch

I am using this query to retrieve latest record based on runId, but this query is giving any result.
#Query("select vr1 from VendorResult vr1 left join VendorResult vr2 on (vr1.applicationId = vr2.applicationId "
+ "and vr1.productType=vr2.productType and vr1.runId<vr2.runId) "
+ "where vr1.applicationId=?1 and vr1.overallStatus=?2 and vr2.applicationId is null")
List<VendorResult> getVendorResults(String applicationId,String overallStatus);
Tried this also
#Query(value = "select vs from VendorResult vs where vs.applicationId=?1 and vs.overallSatatus=?2 and vs.runId = (select max(v.runId) from VendorResult v where v.applicationId = vs.applicationId)",nativeQuery = true)
List<VendorResult> getVendorResults(String applicationId,String overallStatus);
Can you please help me?
a paginated select with a page long 1 and order by id desc will get you what you need
#Query("select vr1 from VendorResult vr1 where vr1.applicationId=?1 and vr1.overallStatus=?2 order by vr1.runId DESC")
List<VendorResult> getVendorResults(String applicationId,String overallStatus, Pageable pageable);
invoke with
repository.getVendorResults(applicationId, overallStatus, PagePageRequest.of(0, 1));

EclipseLink/TopLink full outer join instead left join

On model Card can have or not CardHolder ( 1:1 ), and I would like getting every cards filter by issuer linked to actived cardHolders plus cards without cardHolders, so I need a full outer join. Although the query below translate to left join returning just cards with cardHolders
final ExpressionBuilder builder = new ExpressionBuilder( Card.class );
Expression queryExp = builder.get( "cardIssuer" ).equal( cardIssuer );
queryExp = queryExp.and( builder.get( "cardStatus" ).get( "statusType" ).equal( "ACTIVATED" ) );
queryExp = queryExp.and( builder.getAllowingNull( "cardHolder" )isNull().or(
builder.get( "cardHolder" ).get( "status" ).get( "status" ).equal( "ACTIVE" ) ) );
Expression orderExpression = builder.get( "cardHolder" ).get( "surname" ).descending();
return getMultiple( queryExp, pageable , Card.class, orderExpression );
Translate query is
SELECT COUNT(t0.CARD_ID) FROM CARD t0 LEFT JOIN CARD_HOLDER t3
ON (t3.CARD_HOLDER_ID = t0.CARD_HOLDER_ID), CARD_HOLDER_STATUS t2, CARD_STATUS t1
WHERE (((((t0.CARD_ISSUER_ID = 10006) AND (t1.STATUS_TYPE = 'ACTIVATED')) AND (t2.STATUS = 'ACTIVE'))
AND (t0.CARD_ID IN ('52683','52692')))
AND ((t1.CARD_STATUS_ID = t0.CARD_STATUS_ID) AND (t2.STATUS_ID = t3.STATUS_ID)))
Due to JPA version the outer join is not properly done, so I found a way through native queries
#NamedNativeQueries( {
#NamedNativeQuery( name = Card.USER_DIRECTORY_BASE,
query = "select * from card c full outer join card_holder ch on c.card_holder_id = ch.card_holder_id "
+ "where c.CARD_ISSUER_ID = ?1 and c.card_status_id = 1 and ( ch.STATUS_ID = 1 or c.CARD_HOLDER_ID is null) "
+ "order by ch.FORENAME asc",
resultClass = Card.class ),
#NamedNativeQuery( name = Card.USER_DIRECTORY_BASE_COUNT,
query = "select count(*) from card c full outer join card_holder ch on c.card_holder_id = ch.card_holder_id "
+ "where c.CARD_ISSUER_ID = ?1 and c.card_status_id = 1 and ( ch.STATUS_ID = 1 or c.CARD_HOLDER_ID is null) "
+ "order by ch.FORENAME asc" )
} )
And getting results
Query query = em.createNamedQuery( Card.USER_DIRECTORY_BASE);
query.setParamenter(1,10000);
query.getResultList();

converting sql to hql query

Have to convert below working query into hql query. product and car is the domain.
Product {
long id
hasmany car: Car
string code
}
Car {
int no
int value
}
select p.id from products p
inner join car c on c.id=p.id and p.code='car'
inner join car cr on cr.id=p.id and p.code='car'
where c.no=1 and c.value>=10 and c.no=2 and c.value<=30
Looking at the definition of your model it should be enough to do the following query
var query = session.CreateQuery("select p.id from Product p join p.car c" +
" where c.no=1 " +
" and c.value>=10 " +
" and c.no=2 " +
" and c.value<=30 " +
" and p.code='car' ");
var results = query.List<int>();
with session being instance of ISession interface.

DataMapper: Sorting results by association count (number of related objects)

Given are movies and actors in an m:n relation. What I want to do is retrieve a list of actors, ordered by the number of movies they played in.
class Movie
include DataMapper::Resource
property :id, Serial
property :title, String
has n, :actors, through: Resource
end
class Actor
include DataMapper::Resource
property :name, String, key: true
has n, :movies, through: Resource
end
In pseudo-DM what I want is this:
Actor.all order: [ :movies.count ]
I found another question about sorting by a single attribute of an association but this approach only worked for real properties. Any usable solution would be helpful. Thx!
Taking the answer by Sean Larkin as a starting point I ended up with something like this:
actors = repository(:default).adapter.select(
"SELECT actors.name, count(actor_movies.actor_name) AS count " +
"FROM actors " +
"JOIN actor_movies WHERE actors.name = actor_movies.actor_name " +
"GROUP BY actors.name " +
"ORDER BY count(actor_movies.actor_name) desc " +
"LIMIT 5;"
)
=> [
#<struct name="Samuel L. Jackson", count=66>,
#<struct name="Michael Caine", count=64>,
#<struct name="Robert De Niro", count=59>,
#<struct name="Harvey Keitel", count=58>,
#<struct name="Gene Hackman", count=57>
]
The documentation for DataMapper is little outdated and I struggled trying to accomplish the same thing that you were doing.
I instead used a direct MySQL query:
records = repository(:default).adapter.select(“SELECT * FROM actor ORDER BY count(movies) desc;”)
It is important to note that when you use the direct MySQL query, that a struct is returned rather than just a hash of the data. You will have to convert it into a hash manually if, say you are returning this data as JSON.
You could convert a struct to hash in Ruby 1.8-1.9 via:
actors = repository(:default).adapter.select( "SELECT actors.name, count(actor_movies.actor_name) AS count " + "FROM actors " + "JOIN actor_movies WHERE actors.name = actor_movies.actor_name " + "GROUP BY actors.name " + "ORDER BY count(actor_movies.actor_name) desc " + "LIMIT 5;" ).map{|struct| {:name => struct.name, :count => struct.count}}
In Ruby 2.0, they added to_h so you can use this:
actors = repository(:default).adapter.select( "SELECT actors.name, count(actor_movies.actor_name) AS count " + "FROM actors " + "JOIN actor_movies WHERE actors.name = actor_movies.actor_name " + "GROUP BY actors.name " + "ORDER BY count(actor_movies.actor_name) desc " + "LIMIT 5;" ).map(&:to_h)

Optimize Linq to SQL query, Group By multiple fields

My LINQ query contains the following Group By statement:
Group p By Key = New With { _
.Latitude = p.Address.GeoLocations.FirstOrDefault(Function(g) New String() {"ADDRESS", "POINT"}.Contains(g.Granularity)).Latitude, _
.Longitude = p.Address.GeoLocations.FirstOrDefault(Function(g) New String() {"ADDRESS", "POINT"}.Contains(g.Granularity)).Longitude}
The query works, but here is the SQL that the clause above produces
SELECT [t6].[Latitude]
FROM (
SELECT TOP (1) [t5].[Latitude]
FROM [dbo].[GeoLocations] AS [t5]
WHERE ([t5].[Granularity] IN (#p0, #p1)) AND ([t5].[AddressId] = [t2].[Addr_AddressId])
) AS [t6]
) AS [value], (
SELECT [t8].[Longitude]
FROM (
SELECT TOP (1) [t7].[Longitude]
FROM [dbo].[GeoLocations] AS [t7]
WHERE ([t7].[Granularity] IN (#p2, #p3)) AND ([t7].[AddressId] = [t2].[Addr_AddressId])
) AS [t8]
) AS [value2]
I am not a SQL expert, but it looks to me that this is rather suboptimal translation. This should really be one query that selects Latitide and Longitude from the first record. Perhaps SQL Server Optimizer will take care of this. But is there a way to nudge Linq to generate a leaner SQL statement to begin with?
I tried the following, too..
Group p By Key = p.Address.GeoLocations.Where(Function(g) New String() {"ADDRESS", "POINT"}.Contains(g.Granularity)). _
Select(Function(g) New With {.Latitude = g.Latitude, .Longitude = g.Longitude}).FirstOrDefault
but this produced an error: "A group by expression can only contain non-constant scalars that are comparable by the server."
Sorry to reply in c#...
Here's what you have, translated to c#:
List<string> params = new List<string>()
{ "Address", "Point" };
from p in people
group p by new {
Latitude = p.Address.GeoLocations
.FirstOrDefault(g => params.Contains(g.Granularity)).Latitude,
Longitude = p.Address.GeoLocations
.FirstOrDefault(g => params.Contains(g.Granularity)).Longitude
};
Here's a rewrite, using the let keyword.
from p in people
let loc = p.Address.GeoLocations
.FirstOrDefault(g => params.Contains(g.Granularity))
group p by new
{
Latitude = loc.Latitude,
Longitude = loc.Longitude
};