I need to retrieve items based on a few different restrictions, one is to have code of 234 the other is to have calculated number of less than 10, but I am not sure how to pass values to the sqlRestrictions method.
I am using {alias} but it passes Item rather than city to this.
List<Store> stores = (List<Store>) sessionFactory.getCurrentSession()
.createCriteria(Item.class)
.createAlias("Address", "address")
.createAlias("address.myJoinTable.city", "city")
.setProjection(pl)
.add(Restrictions.eq("address.myJoinTable.city",
session.load(City.class, myId)))
.add(Restrictions
.sqlRestriction("SELECT (
{alias}.id * {alias}.code * "
+ mynumber1 + " * " + mynumber2 + ")
as number from city
HAVING number < 10")
.setResultTransformer(
new AliasToBeanResultTransformer(Store.class))
.list();
You can use public static Criterion sqlRestriction(String sql, Object value, Type type) if you only need to pass one value. Use public static Criterion sqlRestriction(String sql, Object[] values, Type[] types) for multiple values.
For example:
Type[] tipos = {IntegerType.INSTANCE, IntegerType.INSTANCE};
Integer[] values = {1, 2};
criteria.add(Restrictions.sqlRestriction("SELECT ({alias}.id * {alias}.code * ? * ?) AS number FROM city HAVING number < 10", values, tipos ));
Related
Is it possible to write a TypedQuery which contains multiple custom constructors? For example if you want to create objects which contain an id and another object, such as:
TypedQuery<ChapterWithBookId> query = em.createQuery(
"SELECT new " + ChapterWithBookId.class.getName() + "(book.id, new example.Chapter(chapter.id, "
+ "chapter.size"
+ ")) "
+ "FROM " + Book.class.getName() + " AS book JOIN " + Chapter.class.getName() + " AS chapter WHERE "
+ "book.id IN :ids", ChapterWithBookIds.class)
.setParameter("ids", ids);
Disregarding whether this example makes practical sense, this query should return a list of objects of type ChapterWithBookId, where in that list there is an object for each chapter for all books which have an id, contained in the list of ids set as a parameter.
That's right, you can't have two constructor calls in JPQL - it's not Java, but a custom language and only supports "SELECT NEW".
There are several ways to remedy this:
.1. Have your constructor build the second object as well - admittedly an ugly hack:
public ChapterWithBookId(Long bookId, Long chapterId, Long size) {
this.bookId = bookId;
this.chapter = new Chapter(chapterId, size);
}
.2. Just return the result as a tuple, iterate over the list of tuples and build objects (also ugly).
.3. If you use Hibernate, use Hibernate's ResultTransformer:
List<ChapterWithBookId> chapterWithBookDtos = entityManager
.createQuery(
"SELECT book.id, chapter.id, chapter.size "
+ "FROM " + Book.class.getName() + " AS book JOIN " + Chapter.class.getName() + " AS chapter WHERE "
+ "book.id IN :ids", ChapterWithBookIds.class)
.setParameter("ids", ids);)
.unwrap( org.hibernate.query.Query.class )
.setResultTransformer(
new ResultTransformer() {
#Override
public Object transformTuple(
Object[] tuple,
String[] aliases) {
return new ChapterWithBookId(
(Long) tuple[0],
new Chapter((Long) tuple[1], (Long) tuple[2])
);
}
#Override
public List transformList(List collection) {
return collection;
}
}
)
.getResultList();
Here a general article about your options: Hibernate’s ResultTransformer in Hibernate 4, 5 & 6
Blog post about why ResultTransformer also improves query efficiency by Vlad Mihalcea.
And Vlad also offers the hibernate-types library, which has a much nice ListResultTransformer.
I definitely recommend the last two articles and everything Vlad has written - blog and book.
As it's defined in the JPA specification (see section 4.8 SELECT Clause):
The SELECT clause has the following syntax:
select_clause ::= SELECT [DISTINCT] select_item {, select_item}*
select_item ::= select_expression [ [AS] result_variable]
select_expression ::=
single_valued_path_expression |
scalar_expression |
aggregate_expression |
identification_variable |
OBJECT(identification_variable) |
constructor_expression
constructor_expression ::=
NEW constructor_name ( constructor_item {, constructor_item}* )
constructor_item ::=
single_valued_path_expression |
scalar_expression |
aggregate_expression |
identification_variable
aggregate_expression ::=
{ AVG | MAX | MIN | SUM } ([DISTINCT] state_valued_path_expression) |
COUNT ([DISTINCT] identification_variable | state_valued_path_expression |
single_valued_object_path_expression) |
function_invocation
So, as you can see the constructor_item can not be constructor_expression. But you can easy refactor your class ChapterWithBookId constructor to construct example.Chapter by id and size and, IMHO, it will make the JPQL much readable.
I'm trying to optimize some queries, and I have this crazy one. The basic idea is I get a bunch of rooms which has some corresponding meetings. I currently run a query to get all the rooms, then foreach room I need to get the meetings, where I do a query for each room. This opens up for a lot of database connections (i.e. 1000 rooms each having to open a connection to pull the meetings), and I'd like to do it as a batch instead. I am using dapper to map my queries to models and I'm trying to use the list parameters described here
SELECT
mm.id,
mm.organizer_name as Organizer,
mm.subject as Subject,
mm.start_time as StartTime,
mm.end_time as EndTime,
(mm.deleted_at IS NOT NULL) as WasCancelled,
(am.interactive = 0 AND am.cancelled_at IS NOT NULL) as WasNoShow,
c.name as name
FROM master_meeting mm
LEFT JOIN master_meeting__exchange mme ON mme.id=mm.id
LEFT JOIN master_meeting__forwarded_exchange mmfe ON mmfe.id=mm.id
LEFT JOIN meeting_instance__exchange mie ON mie.meeting_id=mm.id
LEFT JOIN meeting_instance__forwarded_exchange mife ON mife.meeting_id=mm.id
LEFT JOIN appointment_meta__exchange ame ON mie.item_id=ame.item_id
LEFT JOIN appointment_meta__exchange ame2 ON mife.item_id=ame2.item_id
LEFT JOIN appointment_meta am ON am.id=ame.id
LEFT JOIN appointment_meta am2 ON am2.id=ame2.id
LEFT JOIN calendar c on mie.calendar_id=c.id
WHERE mie.calendar_id = #Id OR mife.calendar_id=#Id
AND mm.start_time BETWEEN #StartTime AND #EndTime
Without going into details of the crazy long join sequence, I currently have to do this query, a lot. It has been written up initially as:
List<Result> resultSet = new List<Result>();
foreach(int id in idList){
resultSet.AddRange(
_queryHandler.Handle(
new MeetingQuery(id, "FixedStartTime", "FixedEndTime")
)
);
}
Which in turn calls this a bunch of times and runs the query:
_connection.Query<Meeting>(sql,
new {
Id = query.id,
StartTime = query.StartTime,
EndTime = query.EndTime
}
);
This obviously requires quite a few database connections, and I'd like to avoid this by having dapper doing multiple queries, but I get the following error if I try to add the parameters as a list which looks like this:
class Parameters {
int Id;
string StartTime;
string EndTime;
}
List<Parameters> parameters = new List<Parameters>();
foreach(int id in idList)
parameters.Add(new Parameters(id, "SameStartTime", "SameEndTime");
Then I would use the list of parameters as this:
_connection.Query<Meeting>(sql,parameters);
The error I get is:
dapper Additional information: An enumerable sequence of parameters (arrays, lists, etc) is not allowed in this context
Firstly, it's possible to reuse a single connection for multiple queries, so you could retrieve all of your data with multiple Dapper "Query" calls using the same connection.
Something like the following (which isn't the exact same query as you showed since I was testing this on my own computer with a local database; it should be easy enough to see how it could be altered to work with your query, though) -
private static IEnumerable<Record> UnbatchedRetrieval(IEnumerable<Parameters> parameters)
{
var allResults = new List<Record>();
using (var conn = GetConnection())
{
foreach (var parameter in parameters)
{
allResults.AddRange(
conn.Query<Record>(
"SELECT Id, Title FROM Posts WHERE Id = #id",
parameter
)
);
}
}
return allResults;
}
public class Parameters
{
public int Id { get; set; }
}
However, if it really is the number of queries that you want to reduce through batching then there isn't anything in Dapper that makes it very easy to do since each parameter must be uniquely named, which won't be the case if you provide multiple instances of a type as the "parameters" value (since there will be "n" Id values that are all called "Id", for example).
You could do something a bit hacky to produce a single query string that will return results from multiple parameter sets, such as the following -
private static IEnumerable<Record> BatchedRetrieval(IEnumerable<Parameters> parameters)
{
using (var conn = GetConnection)
{
var select = "SELECT Id, Title FROM Posts";
var where = "Id = {0}";
var sqlParameters = new DynamicParameters();
var combinedWheres =
"(" +
string.Join(
") OR (",
parameters.Select((parameter, index) =>
{
sqlParameters.Add("id" + index, parameter.Id);
return string.Format(where, "#id" + index);
})
) +
")";
return conn.Query<Record>(
select + " WHERE " + combinedWheres,
sqlParameters
);
}
}
public class Parameters
{
public int Id { get; set; }
}
.. but this feels a bit dirty. It might be an option to explore, though, if you are absolutely sure that performing those queries one-by-one is a performance bottleneck.
Another thing to consider - when you need the data for 1000 different ids, are the start and end times always the same for each of the 1000 queries? If so, then you could possibly change your query to the following:
private static IEnumerable<Record> EfficientBatchedRetrieval(
IEnumerable<int> ids,
DateTime startTime,
DateTime endTime)
{
using (var conn = GetConnection())
{
return conn.Query<Record>(
#"SELECT
mm.id,
mm.organizer_name as Organizer,
mm.subject as Subject,
mm.start_time as StartTime,
mm.end_time as EndTime,
(mm.deleted_at IS NOT NULL) as WasCancelled,
(am.interactive = 0 AND am.cancelled_at IS NOT NULL) as WasNoShow,
c.name as name
FROM master_meeting mm
LEFT JOIN master_meeting__exchange mme ON mme.id=mm.id
LEFT JOIN master_meeting__forwarded_exchange mmfe ON mmfe.id=mm.id
LEFT JOIN meeting_instance__exchange mie ON mie.meeting_id=mm.id
LEFT JOIN meeting_instance__forwarded_exchange mife ON mife.meeting_id=mm.id
LEFT JOIN appointment_meta__exchange ame ON mie.item_id=ame.item_id
LEFT JOIN appointment_meta__exchange ame2 ON mife.item_id=ame2.item_id
LEFT JOIN appointment_meta am ON am.id=ame.id
LEFT JOIN appointment_meta am2 ON am2.id=ame2.id
LEFT JOIN calendar c on mie.calendar_id=c.id
WHERE mie.calendar_id IN #Ids OR mife.calendar_id IN #Ids
AND mm.start_time BETWEEN #StartTime AND #EndTime",
new { Ids = ids, StartTime = startTime, EndTime = endTime }
);
}
}
There may be a problem with this if you call it with large numbers of ids, though, due to the way that Dapper converts the IN clause - as described in https://stackoverflow.com/a/19938414/3813189 (where someone warns against using it with large sets of values).
If that approach fails then it might be possible to do something similar to the temporary table bulk load suggested here: https://stackoverflow.com/a/9947259/3813189, where you get all of the keys that you want data for into a temporary table and then perform a query that joins on to that table for the keys (and then deletes it again after you have the data).
I have a django model:
class Field:
choice = models.CharField(choices=choices)
value = models.CharField(max_length=255)
In my database I have some cases where there are 3 "fields" with the same choice, and some cases where there is 1 field of that choice
How can I order the queryset so it returns, sorted by choice, but with all ones in a set of 3 at the start?
For example
[1,1,1,3,3,3,4,4,4,2,5] where 1,2,3,4,5 are possible choices?
This is the best I can do using django's ORM. Basically, just like in SQL, you have to construct a custom order_by statement. In our case, we'll place it in the SELECT and then order by it:
1) Get a list of choices sorted by frequency: [1, 3, 4, 2, 5]
freq_list = (
Field.objects.values_list('choice', flat=True)
.annotate(c=Count('id')).order_by('-c', 'choice')
)
2) Add indexes with enumerate: [(0,1), (1,3), (2,4), (3,2), (4,5)]
enum_list = list(enumerate(freq_list))
3) Create a list of cases: ['CASE', 'WHEN choice=1 THEN 0', ..., 'END']
case_list = ['CASE']
case_list += ["WHEN choice={1} THEN {0}".format(*tup) for tup in enum_list]
case_list += ['END']
4) Combine the case list into one string: 'CASE WHEN choice=1 THEN 0 ...'
case_statement = ' '.join(case_list)
5) Finally, use the case statement to select an extra field 'o' which will be corresponding order, then just order by this field
Field.objects.extra(select={'o': case_statement}).order_by('o')
To simplify all this, you can put the above code into a Model Manager:
class FieldManager(models.Manager):
def get_query_set(self):
freq_list = (
Field.objects.values_list('choice', flat=True)
.annotate(c=Count('id')).order_by('-c', 'choice')
)
enum_list = list(enumerate(freq_list))
case_list = ['CASE']
case_list += ["WHEN choice={1} THEN {0}".format(*tup) for tup in enum_list]
case_list += ['END']
case_statement = ' '.join(case_list)
ordered = Field.objects.extra(select={'o': case_statement}).order_by('o')
return ordered
class Field(models.Model):
...
freq_sorted = FieldManager()
Now you can query:
Field.freq_sorted.all()
Which will get you a Field QuerySet sorted by frequency of choices
You should make a function and detect which is repeated to select unique, then calling from mysql as a function over mysql
Let's say I have a table with 3 columns: C1, C2, C3
I make a search based on the C1 column.
Could I make something similar like this (this is not working - because this is not the way prepareStatement it's used:) )
String c;// the name of the column
...
String sql = "select * from table where ? = ?";
pre = con.prepareStatement(sql);
pre.setString(1, c);
pre.setString(1, i);
rs = pre.executeQuery();
The main idea, I don't want to have 3 ifs for every column. An elegant solution?
This won't work. The prepare statement parses the SQL, sends to the database for validation and compilation. If question marks could substitute parts of the SQL, you would loose the whole point of bound variables - speed and security. You would reintroduce SQL injection back and statements will have to be recompiled for all parameters.
Wouldn't something like SELECT * FROM table WHERE c1 = ? OR c2 = ? OR c3 = ? be better (of course depending on indexes and table sizes).
you could code up a a set of sql queries and store them in a map, then grab one based on the column in question.
enum column { a, b, c}
Map<column, string> str;
static {
str.put(a, "select * from tbl where a = ? ");
...
}
then just grab one out of the map later based on the enum. String appends in sql statements have a way of becoming security problems in the future.
Use a dynamic query and a java.sql.Statement:
String whereClause = c + " = " + i;
// Form the dynamic Query
StringBuffer query = new StringBuffer( "SELECT * FROM TABLE" );
// Add WHERE clause if any
query.append(" WHERE " + whereClause);
// Create a SQL statement context to execute the Query
Statement stmt = con.createStatement();
// Execute the formed query and obtain the ResultSet
ResultSet resultSet = stmt.executeQuery(query.toString());
can't you do this:
String c;// the name of the column
...
String sql = "select * from table where " + c + " = ?";
pre = con.prepareStatement(sql);
pre.setString(1, i);
rs = pre.executeQuery();
?
If not then this might be a solution:
String c;// the name of the column
...
String sql = "select * from table where ('C1' = ? AND C1 = ?)
OR ('C2' = ? AND C2 = ?)
OR ('C3' = ? AND C3 = ?)"
pre = con.prepareStatement(sql);
pre.setString(1, c);
pre.setString(2, i);
pre.setString(3, c);
pre.setString(4, i);
pre.setString(5, c);
pre.setString(6, i);
rs = pre.executeQuery();
I am trying to accomplish the following. Let's say we have a table that contains these fields (ID, content)
1 | apple
2 | pineapple
3 | application
4 | nation
now, I am looking for a function that will tell me all possible common matches. For example, if the argument is "3", the function will return all possible strings from 3 characters that appear in more then one record.
In this case, I get "app","ppl","ple","ati","tio","ion"
If the argument is "4", i get: "appl","pple","atio","tion"
If the arugment is "5", i get: "apple","ation"
If the argument is "6", nohting is returned.
Untill now, I did not find a function that accomplishes this.
Thx!
Some extra information:
I am using this in a PHP script with a MySQL database. I really just want to give the amount of characters as an argument and of course the table to search in.
Well, this is kind of ugly, but it does work fine. It's generic SQL and will work in any environment. Simply generate a number of selects of a substring that is greater than the maximum length of the field that you're reading. Change the number 50 in the function to a number that exceeds your fieldlength. It may return a realllly long query, but like I said, it'll work fine. Here is an example in Python:
import sqlite3
c = sqlite3.connect('test.db')
c.execute('create table myTable (id integer, content varchar[50])')
for id, content in ((1,'apple'),(2,'pineapple'),(3,'application'),(4,'nation')):
c.execute('insert into myTable values (?,?)', [id,content])
c.commit();
def GenerateSQL(substrSize):
subqueries = ["select substr(content,%i,%i) AS substr, count(*) AS myCount from myTable where length(substr(content,%i,%i))=%i group by substr(content,%i,%i) " % (i,substrSize,i,substrSize,substrSize,i,substrSize) for i in range(50)]
sql = 'select substr FROM \n\t(' + '\n\tunion all '.join(subqueries) + ') \nGROUP BY substr HAVING sum(myCount) > 1'
return sql
print GenerateSQL(3)
print c.execute(GenerateSQL(3)).fetchall()
The query generated looks like:
select substr FROM
(select substr(content,0,3) AS substr, count(*) AS myCount from myTable where length(substr(content,0,3))=3 group by substr(content,0,3)
union all select substr(content,1,3) AS substr, count(*) AS myCount from myTable where length(substr(content,1,3))=3 group by substr(content,1,3)
union all select substr(content,2,3) AS substr, count(*) AS myCount from myTable where length(substr(content,2,3))=3 group by substr(content,2,3)
union all select substr(content,3,3) AS substr, count(*) AS myCount from myTable where length(substr(content,3,3))=3 group by substr(content,3,3)
union all select substr(content,4,3) AS substr, count(*) AS myCount from myTable where length(substr(content,4,3))=3 group by substr(content,4,3)
... )
GROUP BY substr HAVING sum(myCount) > 1
And the results it produces are:
[(u'app',), (u'ati',), (u'ion',), (u'nat',), (u'pin',), (u'ple',), (u'ppl',), (u'tio',)]
I'm sorry as I haven't been playing with php for a while & I don't have a proper test environment for it, but I quickly devised a way of doing this in c# 3.5
pseudocode: build a table with strings of the specified length & a count of occurences next to it. Select where count > 1:
static void Main(string[] args)
{
string[] data = { "apple", "pinapple", "application", "nation" };
string[] result = my_func(3,data);
foreach (string str in result)
{
Console.WriteLine(str);
}
Console.ReadKey();
}
private static string[] my_func(int l, string[] data)
{
Dictionary<string,int> dict = new Dictionary<string,int>();
foreach (string str in data)
{
for (int i = 0; i < str.Length - l + 1; i++)
{
string part = str.Substring(i, l);
if (dict.ContainsKey(part))
{
dict[part]++;
}else {
dict.Add(part,1);
}
}
}
var result = from k in dict.Keys
where dict[k] > 1
orderby dict[k] descending
select k;
return result.ToArray<string>();
}
One obvious option is to use REGEX. I have no prior experience in this but this might be of help to you:
http://dev.mysql.com/doc/refman/5.1/en/regexp.html
You'll need to find a suitable expression to match what you need.