Limit query result by type - mysql

I have a table with 2 different types (image and video). I would like to get max 2 rows of each type... any help on this? So that the resultset will be 4 rows.
This is the query I have at the moment (this just get's the rows from 1 type; image):
SELECT DISTINCT
mm.mm_id,
mm.mm_title,
mm.mm_hash
FROM
boomla_multimedia mm,
boomla_multimedia_domain md
WHERE mm.mm_id = md.mm_id
AND cat_id = 4
AND md.dom_id = 26
AND mm.mm_published = 1
AND mm.mm_media_type = 'image'
ORDER BY mm.mm_id DESC
LIMIT 0, 2;

Make two separate queries and use a UNION of the two result sets.
SELECT DISTINCT mm.mm_id, mm.mm_title, mm.mm_hash FROM boomla_multimedia mm,
boomla_multimedia_domain md
WHERE mm.mm_id = md.mm_id AND cat_id = 4 AND md.dom_id = 26 AND mm.mm_published = 1
AND mm.mm_media_type = 'image' ORDER BY mm.mm_id DESC LIMIT 0, 2
UNION
-- add here the select for 'video' type

Related

query select max with mixed string and int

I have a table which contain a column for generated code, the data type is VARCHAR with mixed string/int values like :
Table demo
ID code
==============
1 | 001qwe
2 | 002qwe
3 | 001asd
Question :
1. How to get max value that contain qwe or asd, i want it used as filter.
2. How to get id of row which contain the maxed value
i want something like :
select *,MAX(SUBSTRING(code, 1, 3)) from demo where SUBSTRING(code, 4, 3) = 'asd'
Yes this case code length is 6 and number is 3 digit in the beginning of data
Considering above you can write your query as below
select *,left(`code`,3)
from demo
order by left(`code`,3) * 1 desc
limit 1
DEMO
to get individual results you can use following
SELECT a.id qweid, a.code qwecode,b.id asdid,b.code asdcode
FROM
(SELECT id,`code`
FROM demo
WHERE RIGHT(`code`,3) = 'qwe'
ORDER BY LEFT(`code`,3) * 1 DESC
LIMIT 1) a
CROSS JOIN(SELECT id,`code`
FROM demo
WHERE RIGHT(`code`,3) = 'asd'
ORDER BY LEFT(`code`,3) * 1 DESC
LIMIT 1) b
DEMO

Conditional condition in ON clause

I am trying to apply a conditional condition inside ON clause of a LEFT JOIN. What I am trying to achieve is somewhat like this:
Pseudo Code
SELECT * FROM item AS i
LEFT JOIN sales AS s ON i.sku = s.item_no
AND (some condition)
AND (
IF (s.type = 0 AND s.code = 'me')
ELSEIF (s.type = 1 AND s.code = 'my-group')
ELSEIF (s.type = 2)
)
I want the query to return the row, if it matches any one of the conditions (Edit: and if it matches one, should omit the rest for the same item).
Sample Data
Sales
item_no | type | code | price
1 0 me 10
1 1 my-group 12
1 2 14
2 1 my-group 20
2 2 22
3 2 30
4 0 not-me 40
I want the query to return
item_no | type | code | price
1 0 me 10
2 1 my-group 20
3 2 30
Edit: The sales is table is used to apply special prices for individual users, user groups, and/or all users.
if type = 0, code contains username. (for a single user)
if type = 1, code contains user-group. (for users in a group)
if type = 2, code contains empty-string (for all users).
Use the following SQL (assumed, the the table sales has a unique id field as usual in yii):
SELECT * FROM item AS i
LEFT JOIN sales AS s ON i.sku = s.item_no
AND id = (
SELECT id FROM sales
WHERE item_no = i.sku
AND (type = 0 AND code = 'me' OR
type = 1 AND code = 'my-group' OR
type = 2)
ORDER BY type
LIMIT 1
)
Try following -
SELECT *,SUBSTRING_INDEX(GROUP_CONCAT(s.type ORDER BY s.type),','1) AS `type`, SUBSTRING_INDEX(GROUP_CONCAT(s.code ORDER BY s.type),','1) AS `code`,SUBSTRING_INDEX(GROUP_CONCAT(s.price ORDER BY s.type),','1) AS `price`
FROM item AS i
LEFT JOIN sales AS s
ON i.sku = s.item_no AND (SOME CONDITION)
GROUP BY i.sku

Is there better way to do this query?

SELECT *
FROM a
WHERE a.re_id = 3443499
AND a.id IN
(
SELECT b.rsp_id FROM b
WHERE b.f_id = 9
GROUP BY b.rsp_id
HAVING FIND_IN_SET(16, GROUP_CONCAT(b.o_id)) > 0
AND FIND_IN_SET(15, GROUP_CONCAT(b.o_id)) > 0
UNION
SELECT b.rsp_id FROM b
WHERE b.f_id = 4
GROUP BY b.rsp_id
HAVING FIND_IN_SET(5, GROUP_CONCAT(b.o_id)) > 0
)
ORDER BY id DESC
Here "f_id" is array and its values are those in first parameter of "FIND_IN_SET" function.
For example
9=>(
16,
15
),
4=>(
5
)
Sample data for those 2 folumns in table b, 2 columns f_id and o_id
f_id o_id
9 15
9 18
9 23
4 5
3 8
The gist of this answer is that the current query does not run. So, fix the syntax and ask another question.
First, you could write the query so it is syntactically correct. The query will fail as written, because the first subquery returns at least two rows and the second only one.
Second, use UNION ALL instead of UNION, unless you specifically want to incur the overhead of removing duplicates.
Third, the ORDER BY will generate an error.
Fourth, the GROUP_CONCAT() is dangerous and unnecessary.
I'm not 100% sure this is the intention, but I would start with a query like this:
SELECT a.id, a.re_id
FROM a
WHERE a.re_id = 3443499 AND
a.id IN (SELECT b.rsp_id
FROM b
WHERE b.f_id = 9
GROUP BY b.rsp_id
HAVING MAX(b.o_id = 16) > 0 AND
MAX(b.o_id = 15) > 0
)
UNION ALL
SELECT b.rsp_id, NULL
FROM b
WHERE b.f_id = 4
GROUP BY b.rsp_id
HAVING MAX(b.o_id = 5) > 0
ORDER BY id;
Then, if you want this optimized, I would suggest asking another question, along with relevant information about the table structures and current performance.

Count first occurence with column value ordered by another column

I have an assigns table with the following columns:
id - int
id_lead - int
id_source - int
date_assigned - int (this represents a unix timestamp)
Now, lets say I have the following data in this table:
id id_lead id_source date_assigned
1 20 5 1462544612
2 20 6 1462544624
3 22 6 1462544615
4 22 5 1462544626
5 22 7 1462544632
6 25 6 1462544614
7 25 8 1462544621
Now, lets say I want to get a count of the rows whose id_source is 6, and is the first entry for each lead (sorted by date_assigned asc).
So in this case, the count would = 2, because there are 2 leads (id_lead 22 and 25) whose first id_source is 6.
How would I write this query so that it is fast and would work fine as a subquery select? I was thinking something like this which doesn't work:
select count(*) from `assigns` where `id_source`=6 order by `date_assigned` asc limit 1
I have no idea how to write this query in an optimal way. Any help would be appreciated.
Pseudocode:
select rows
with a.id_source = 6
but only if
there do not exist any row
with same id_lead
and smaller date_assigned
Translate it to SQL
select * -- select rows
from assigns a
where a.id_source = 6 -- with a.id_source = 6
and not exists ( -- but only if there do not exist any row
select 1
from assigns a1
where a1.id_lead = a.id_lead -- with same id_lead
and a1.date_assigned < a.date_assigned -- and smaller date_assigned
)
Now replace select * with select count(*) and you'll get your result.
http://sqlfiddle.com/#!9/3dc0f5/7
Update:
The NOT-EXIST query can be rewritten to an excluding LEFT JOIN query:
select count(*)
from assigns a
left join assigns a1
on a1.id_lead = a.id_lead
and a1.date_assigned < a.date_assigned
where a.id_source = 6
and a1.id_lead is null
If you want to get the count for all values of id_source, the folowing query might be the fastest:
select a.id_source, count(1)
from (
select a1.id_lead, min(a1.date_assigned) date_assigned
from assigns a1
group by a1.id_lead
) a1
join assigns a
on a.id_lead = a1.id_lead
and a.date_assigned = a1.date_assigned
group by a.id_source
You still can replace group by a.id_source with where a.id_source = 6.
The queries need indexes on assigns(id_source) and assigns(id_lead, date_assigned).
Simple query for that would be
check here http://sqlfiddle.com/#!9/8666e0/7
select count(*) from
(select * from assigns group by id_lead )t
where t.id_source=6

Select duplicates/repeating values, ungrouped

I am trying to return a list of ungrouped values having a max defined number of repeating values. I have a list of values retrieved as such:
select TagDirID from tags where id = '550'
The results would be:
9508
10382
10672
65454
65454
65454
65454
As you can see there are 4 repeats of 65454. I would like to return a list that has a user defined max number of repeats for a TagDirID. For instance, selecting only 3 repeats or less:
9508
10382
10672
65454
65454
65454
All the methods I've found return a grouped list, i would like to maintain the individual items. Is this something that can be done in a query? There is a primary key available, TagID.
edit: what this does is select all the tags for an item, id = '550'. because it's user content sometimes people tag the same things multiple times and i'm trying to cut down the duplicates i show.
edit 2: so, while the accepted answer worked for me i found it was a little too slow for what i needed so i came up with a php solution:
function get_tags($ID = '', $tags_to_keep = 3)
{
// Select all tags.
$query = "select TagDirID, Tag from tags where id = '$ID'";
$tags_result = mysql_query($query);
$num_results = mysql_num_rows($tags_result);
for ($i=0; $i<$num_results; $i++)
{
//Get tag topics
$tags_row = mysql_fetch_array($tags_result);
//build array of items already found with counts
$tags_count = array_count_values($tags_filter);
//if number of tags already found($tags_count/$tags_filter) is less than or equal to tags_to_keep then add to filtered array and return array.
if($tags_count[$tags_row['TagDirID']] <= $tags_to_keep)
{
$tags_filter[$i] = $tags_row['TagDirID'];
$tags[$i] = $tags_row['Tag'];
}
}
return $tags;
}
Select TagID, TagDirID
From (
Select T1.TagID, T1.TagDirID
, (Select Count(*)
From tags As T2
Where T2.TagDirID = T1.TagDirID
And T2.TagID < T1.TagID) As Rnk
Where T1.id = '550'
From tags As T1
) As T
Where T.Rnk < 3
Another way of writing the same query:
Select TagID, TagDirID
From (
Select T1.TagID, T1.TagDirID, Count( T2.TagID ) As Rnk
From tags As T1
Left Join tags As T2
On T2.TagDirID = T1.TagDirID
And T2.TagID < T1.TagID
Where T1.id = '550'
Group By T1.TagID, T1.TagDirID
) As T
Where T.Rnk < 3
The approach here is to mimic a ranking function which would sequence the rows for each grouping of TagDirID. Thus, the inner query in either of the two above solutions should give you something like:
TagID | TagDirID | Rnk
1 | 9508 | 0
2 | 10382 | 0
3 | 10672 | 0
4 | 65454 | 0
5 | 65454 | 1
6 | 65454 | 2
7 | 65454 | 3
With the rows numbered within each grouping, we can now filter down our results so that we only get a maximum number of rows in any given group. The ISO/ANSI solution would be to use the ROW_NUMBER ranking function which isn't yet supported by MySQL.
As the repeated values are all equal, you can query for the number of repetions of distinct items. It would be something like this, for 3 items or less:
SELECT T.tdid, T.cnt
FROM (
SELECT distinct(TagDirID) as tdid,
(SELECT COUNT(*) FROM tags WHERE id = tdid) as cnt
FROM tags
) as T
WHERE T.tdid = '550' AND T.cnt < 3;
So the result would be a little different from what you had (not the duplicate items, but one item and the number of duplications), but I think it will do.