Sql statement with multi ANDs querying the same column - sql

I don't know if the title of the post is the appropriate. I have the following table
and an Array in php with some items, parsed_array. What I want to do is to find all the SupermarketIDs which have all the items of the parsed_array.
For example, if parsed_array contains [111,121,131] I want the result to be 21 which is the ID of the Supermarket that contains all these items.
I tried to do it like that:
$this->db->select('SupermarketID');
$this->db->from('productinsupermarket');
for ($i=0; $i<sizeof($parsed_array); $i++)
{
$this->db->where('ItemID', $parsed_array[$i]);
}
$query = $this->db->get();
return $query->result_array();
If there is only one item in the parsed_array the result is correct because the above is equal to
SELECT SupermarketID
FROM productinsupermarket
WHERE ItemID=parsed_array[0];
but if there are more than one items, lets say two, is equal to
SELECT SupermarketID
FROM productinsupermarket
WHERE ItemID=parsed_array[0]
AND ItemID=parsed_array[1];
which of course return an empty table. Any idea how can this be solved?

There are at least two ways of generating the result you want, either a self join (no fun to generate with a dynamic number of items) or using IN, GROUP BY and HAVING.
I can't really tell you how to generate it using CodeIgniter, I assume you're better at that than I am :)
SELECT SupermarketID
FROM productinsupermarket
WHERE ItemID IN (111,121,131) -- The 3 item id's you're looking for
GROUP BY SupermarketID
HAVING COUNT(ItemId) = 3; -- All 3 must match
An SQLfiddle to test with.
EDIT: As #ypercube mentions below, if the ItemId can show up more than once for a SupermarketID, you'll want to use COUNT(DISTINCT ItemId) to count only unique rows instead of counting every occurrence.

You can use where_in in codeigniter as below,
if(count($parsed_array) > 0)
{
$this->db->where_in('ItemID', $parsed_array);
}
Active record class in codeigniter

Try an IN clause or multiple ORs:
SELECT SupermarketID
FROM productinsupermarket
WHERE ItemID=parsed_array[0]
OR ItemID=parsed_array[1];

Related

Rails: Need to scope by max version

I have this problem, I've got database table that looks like this:
"63";"CLINICAL...";"Please...";Blah...;"2014-09-23 13:15:59";37;8
"64";"CLINICAL...";"Please...";Blah...;"2014-09-23 13:22:51";37;9
The values that matter are the second to last and last one.
As you can see, the second to last (abstract_category_numbers) are the same, but the last differs (version_numbers)
Here is the problem:
When I make a scope, it returns all of the records, which i need to focus on the one with the maximum version number.
In SQL i would do something like this:
'SELECT * FROM Category c WHERE
NOT EXISTS SELECT * FROM Category c1
WHERE c.version_number < c1.version_number
AND c.abstract_category_id = c1.abstract_category_id'
But i'm totally lost at Ruby, more specifically how to do this kind of select in the scope (I understand it should be a relation)
Thanks
We can create a scope to select the category with max version_number like this:
scope :with_max_version_number, -> {
joins("JOIN ( SELECT abstract_category_id, max(version_number) AS max_version
FROM categories
GROUP BY abstract_category_id
) AS temp
ON temp.abstract_category_id = categories.abstract_category_id
AND temp.max_version = categories.version_number"
)
}
Basically, we will select the category with the max_version value on temp table in the subquery.
Btw, I expect the table name is categories, you may correct it. Then the final query will be:
Category.with_max_version_number
Scopes are suppose to return an array of values even if there is only 1 record.
If you want to ALWAYS return 1 value, use a static method instead.
def self.max_abstract_category
<your_scope>.max_by{ |obj| obj.version_number }
end
If I understand your question: you have a database table with a version_number column, which rails represents using an Active Record model--that I'll call Category because I don't know what you've called it--and you want to find the single Category record with the largest version_number?
Category.all.order(version_numbers: :DESC).limit(1).first
This query asks for all Category records ordered by version_number from highest to lowest and limits the request to one record (the first record, a.k.a the highest). Because the result of this request is an array containing one record, we call .first on the request to simply return the record.
As far as I'm aware, a scope is simply a named query (I don't actually use scopes). I think you can save this query as a scope by adding the following to your Category model. This rails guide explains more about Scopes.
scope :highest_version, -> { all.order(version_numbers: :DESC).limit(1).first }
I join implementation with baby_squeel but for some reason it was very slow on mysql. So I ended up with something like:
scope :only_latest, -> do
where(%{
NOT EXISTS (SELECT * FROM Category c
WHERE categories.version_number < version_number
AND categories.abstract_category_id = abstract_category_id')
}
end
I filed a BabySqueel bug as I spent a long time trying to do in a code proper way to no avail.

Linq to SQL select one of every different result

Let's say I have a table with two columns, name and template. Let's say i have multiple rows with the name foo and multiple rows with the name bar. All of them have template 3.
How do i perform a linq to SQL select that returns only one of each name with template number 3.
(from f in g_lisFilters where f.Template == "3" orderby f.Sortering select f).ToList();
above code returns a list of all items with template number 3. How do i select only one of every name?
Thanks in advance
You simply have to group the data by template, and take the first element of each group.
var firsts = from e in g_lisFilters
where f.Template == "3"
group by e.Sortering
into groups
select groups.First();
Or
var firsts = g_lisFilters
.Where(e.Template="3")
.GroupBy(e=>e.Sortering ,(key,g)=>g.First());
Have you tried using the 'Distinct' method? It ignores multiple results of the same.
Maybe this could help you:
http://msdn.microsoft.com/en-us/library/vstudio/bb348436(v=vs.100).aspx

Paging in SQL with LIMIT/OFFSET sometimes results in duplicates on different pages

I'm developing an online gallery with voting and have a separate table for pictures and votes (for every vote I'm storing the ID of the picture and the ID of the voter). The tables related like this: PICTURE <--(1:n, using VOTE.picture_id)-- VOTE. I would like to query the pictures table and sort the output by votes number. This is what I do:
SELECT
picture.votes_number,
picture.creation_date,
picture.author_id,
picture.author_nickname,
picture.id,
picture.url,
picture.name,
picture.width,
picture.height,
coalesce(anon_1."totalVotes", 0)
FROM picture
LEFT OUTER JOIN
(SELECT
vote.picture_id as pid,
count(*) AS "totalVotes"
FROM vote
WHERE vote.device_id = <this is the query parameter> GROUP BY pid) AS anon_1
ON picture.id = anon_1.pid
ORDER BY picture.votes_number DESC
LIMIT 10
OFFSET 0
OFFSET is different for different pages, of course.
However, there are pictures with the same ID that are displayed on the different pages. I guess the reason is the sorting, but can't construct any better query, which will not allow duplicates. Could anybody give me a hint?
Thanks in advance!
Do you execute one query per page to display? If yes, I suspect that the database doesn't guarantee a consitent order for items with the same number of votes. So first query may return { item 1, item 2 } and a 2nd query may return { item 2, item 1} if both items have same number of votes. If the items are actually items 10 and 11, then the same item may appear on page 1 and then on page 2.
I had such a problem once. If that's also your case, append an extra clause to the order by to ensure a consistent ordering of items with same vote number, e.g.:
ORDER BY picture.vote, picture.ID
The simples explanation is that you had some data added or some votes occured when you was looking at different pages.
I am sure if you would sorte by ID or creation_date this issue would go away.
I.e. there is no issue with your code
in my case this problem was due to the Null value in the Order By clause, i solved this by adding another Unique ID field in Order By Clause along with other field.

NHibernate How to Select distinct objects based on specific property using HQL?

How can HQL be used to select specific objects that meet a certain criteria?
We've tried the following to generate a list of top ten subscribed RSS feeds (where SubscriptionCount is a derived property):
var topTen = UoW.Session.CreateQuery( #"SELECT distinct rss
FROM RssFeedSubscription rss
group by rss.FeedUrl
order by rss.SubscriptionCount DESC
")
.SetMaxResults(10)
.List<RssFeedSubscription>();
Where the intention is only to select the two unique feed URLs in the database, rather than the ten rows int the database instantiated as objects. The result of the above is:
Column 'RssSubscriptions.Id' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
ORDER BY items must appear in the select list if SELECT DISTINCT is specified.
It's possible just to thin out the results so that we take out the two unique feed URLs after we get the data back from the database, but there must be a way to do this at the DB level using HQL?
EDIT: We realise it's possible to do a Scalar query and then manually pull out values, but is there not a way of simply specifying a match criteria for objects pulled back?
If you change your HQL a bit to look like that:
var topTen = UoW.Session.CreateQuery( #"SELECT distinct rss.FeedUrl
FROM RssFeedSubscription rss
group by rss.FeedUrl
order by rss.SubscriptionCount DESC
")
.SetMaxResults(10)
.List();
the topTen variable will be an object[] with 2 elements in there being the 2 feed URLs.
You can have this returned as strongly typed collection if you use the SetResultTransformer() method of the IQuery interfase.
You need to perform a scalar query. Here is an example from the NHibernate docs:
IEnumerable results = sess.Enumerable(
"select cat.Color, min(cat.Birthdate), count(cat) from Cat cat " +
"group by cat.Color"
);
foreach ( object[] row in results )
{
Color type = (Color) row[0];
DateTime oldest = (DateTime) row[1];
int count = (int) row[2];
.....
}
It's the group by rss.FeedUrl that's causing you the problem. It doesn't look like you need it since you're selecting the entities themselves. Remove that and I think you'll be good.
EDIT - My apologies I didn't notice the part about the "derived property". By that I assume you mean it's not a Hibernate-mapped property and, thus doesn't actually have a column in the table? That would explain the second error message you received in your query. You may need to remove the "order by" clause as well and do your sorting in Java if that's the case.

SQLite design question

Similar to a feed reader, I'm storing a bunch of articles, each pertaining to a source (feed) and each feed can belong to a category. What I'm trying to do is:
Retrieve the articles of the feeds that belong to a certain category.
Group the articles. One scenario would be by date(published_time), so that I have groups, for example: (12.04.09 - 3 articles, 17.04.09 - 9 articles, and so on)
Loop through each group and display each article. Pseudo-code:
foreach (Group group in results)
{
print(group.Name);
foreach (Article article in g.Articles)
{
print(article.Title);
print(article.Content);
}
}
I thought something simple like:
SELECT group_concat(item_id, '#') FROM items GROUP BY date(published_time)
would work. But then I'd have to split the resulting rows and loop through that (and there is no group_concat(*) function)
I'm confused as to how I would group(2) the results so that I can iterate through each one, preserving the group name. I thought that a SQL query returns ONE big table, and so, it seems to be impossible to accomplish this with just one query.
I reckon this is more of a DB design question, I'm also new to SQLite (SQL for that matter), so I ask you, gurus, how would one get this done efficiently?
SELECT Title, Content, date(published_time) AS Date
FROM items
ORDER BY date(published_time);
Pseudocode:
last = None
for r in results:
if not last or r.Date != last.Date:
print "Group", r.Date
print r.Title, r.Content
last = r