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.
Related
There 3 entities: 1) Question, 2) Tag and join table between them - question_has_tag.
When I make a select query like:
select * from question_has_tag as qht where qht.question_id = 6;
I'm getting the following result:
question_id| tag_id
6 | 1
6 | 2
6 | 3
and I needed to get:
question_id| tag_id
6 | 1, 2, 3
How to get it ?
You need to GROUP them and use GROUP_CONCAT
Like
SELECT question_id,GROUP_CONCAT(tag_id ORDER BY tag_id )
FROM question_has_tag as qht
WHERE qht.question_id = 6
GROUP BY question_id;
You're looking for the group_concat() function. This function aggregates a particular field, separating them with a arbitrary character(s).
select
question_id
, group_concat(
distinct tag_id
order by tag_id
separator ','
) as tag_id
from
question_has_tags
where
question_id = 6;
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
i have a table like this on a mysql database:
id | item
-----------
1 | 2
2 | 2
3 | 4
4 | 5
5 | 8
6 | 8
7 | 8
i want the result to be 3 record with the highest Item value
select max(item) returns only 1 value
how can i select multiple max values?
thank you
You can use a derived table to get the maximum value and join it back to the original table to see all rows corresponding to it.
select t.id, t.item
from tablename t
join (select max(item) as mxitem from tablename) x
on x.mxitem = t.item
Edit:
select t.co_travelers_id, t.booking_id, t.accounts_id
from a_co_travelers t
join (select accounts_id, max(booking_id) as mxitem
from a_co_travelers
group by accounts_id) x
on x.mxitem = t.booking_id and t.accounts_id = x.accounts_id
If you use an 'aggregate function' without GROUP BY only one row will be returned.
You may use GROUP BY , with aggregate functions.
Here is SQLFiddle Demo
SELECT id,max(item) AS item
FROM table_name
GROUP BY id
ORDER BY item DESC
LIMIT 3
Hope this helps.
There is the graphical explanation.
There is script mysql (low abstraction level, no inner join or sth)
select * from ocena, uczen where ocena.ocena = (SELECT MAX(ocena.ocena) FROM ocena WHERE ocena.przedmiot_id="4" and ocena.uczen_id="1") and ocena.uczen_id=uczen.id and ocena.przedmiot_id="4" and uczen_id="1"
I wrote this query, it does almost what I want:
SELECT * FROM
(
SELECT COUNT(*) as cnt,
lat,
lon,
elev,
GROUP_CONCAT(CONCAT(usaf,'-',wban))
FROM `ISH-HISTORY_HASPOS`
GROUP BY lat,lon,elev
) AS x WHERE cnt >=1;
output:
+-----+--------+----------+--------+-------------------------------------------------+
| cnt | lat | lon | elev | GROUP_CONCAT(CONCAT(usaf,'-',wban)) |
+-----+--------+----------+--------+-------------------------------------------------+
| 4 | 30.478 | -87.187 | 36 | 722220-13899,722221-13899,722223-13899,999999-13899 |
| 4 | 36.134 | -80.222 | 295.7 | 723190-93807,723191-93807,723193-93807,999999-93807 |
| 5 | 37.087 | -84.077 | 369.1 | 723290-03849,723291-03849,723293-03849,724243-03849,999999-03849 |
| 5 | 38.417 | -113.017 | 1534.1 | 745200-23176,745201-23176,999999-23176,724757-23176,724797-23176 |
| 4 | 40.217 | -76.851 | 105.8 | 999999-14751,725110-14751,725111-14751,725118-14751 |
+-----+--------+----------+--------+-------------------------------------------------+
This returns a concatenated list of stations that are located at identical coordinates. However, I am only interested in concatenating stations with adjoining date ranges. The table that I select from (ISH-HISTORY_HASPOS) has two datetime columns : 'begin' and 'end'. I need the values for these two columns to be within 3 days of each other to satisfy the GROUP_CONCAT conditions.
Edit: In order for a station to be included in the final result's GROUP_CONCAT it must satisfy the following conditions:
It must be co-located with another station in the list (group by
lat,lon,elev)
Its end time must be within 3 days of another station's begin time OR its begin time must be within 3 days of another station's
end time. When I say "another station", I am referring to stations
that are co-located (meet the conditions for #1).
I figure that I will have to use a subquery but I can't seem to figure out how to do it. Some help would be greatly appreciated! Either a query or a stored procedure would be great but a php solution would also be acceptable.
Here is a dump of the table that I am querying:sql dump
The results should look the same as my example, but non-adjoining items (date-wise) should not be there.
A solution could be using a subquery to compute the list of station within 3 days of each other and adding this subquery as a where clause to the main query.
The subquery consists of a cartesian product to list all possible station couples with a first condition to get just the first half of the resulting matrix and two conditions to specify the time constraints. As to these latter conditions I just guessed them, I don't really know the begin and end fields unit of measure.
The resulting query could be this:
SELECT * FROM (
SELECT COUNT(*) AS
cnt,
lat,
lon,
elev,
GROUP_CONCAT(CONCAT(usaf, '-', wban))
FROM ISH-HISTORY_HASPOS
WHERE id IN (
SELECT DISTINCT t1.id
FROM ISH-HISTORY_HASPOS t1
INNER JOIN ISH-HISTORY_HASPOS t2
ON t1.lon = t2.lon
AND t1.lat = t2.lat
AND t1.elev = t2.elev
WHERE t1.id < t2.id
AND abs(t1.begin - t2.end) < 259200
AND abs(t1.end - t2.begin) < 259200
UNION
SELECT DISTINCT t2.id
FROM ISH-HISTORY_HASPOS t1
INNER JOIN ISH-HISTORY_HASPOS t2
ON t1.lon = t2.lon
AND t1.lat = t2.lat
AND t1.elev = t2.elev
WHERE t1.id < t2.id
AND abs(t1.begin - t2.end) < 259200
AND abs(t1.end - t2.begin) < 259200
)
GROUP BY lat, lon, elev
) AS x WHERE cnt >= 1;
I only have access and knowledge of SQL Server so I can't get your data to work and I don't know if MySQL has the equivalent functionality but here is a verbal description of what you need to do.
You need a recursive statement (WITH CTE in SQL Server) to join the table to itself on lat, lon, elev and begin BETWEEN end -3 AND end +3. You will need to be careful not to get caught in an infinite loop - I suggest building a comma seperated list of the IDs you have visited and checking this as you go. Its painful but keep this list in ID order becuase it is what you will need to group on at the end. You also need to keep track of your depth and the original id.
Something like ...
WITH cte(id, idlist, lat, lon, elev, starts, ends)
AS (
SELECT id, CAST(id AS varchar), lat, lon, elev, starts, ends
FROM `ISH-HISTORY_HASPOS`
UNION ALL
SELECT i.id, FunctionToManagetheList(i.idlist, cte.id), lat, lon, elev, starts, ends
FROM `ISH-HISTORY_HASPOS` i
INNER JOIN
cte ON i.lat=cte.lat AND
i.lon=cte.lon AND
i.elev=cte.elev AND
NOT FunctionToCheckIfTheIDisintheLitst(i.id, cte.idlist)
)
SELECT stuffyouneed
FROM `ISH-HISTORY_HASPOS` i
INNER JOIN
(SELECT id, MAX(depth) AS MaxDepth
FROM cte
GROUP BY id) cte1 ON i.id=cte.id
INNER JOIN
cte cte2 ON cte1.id=cte2.id AND cte1.MaxDepth=cte2.Depth
GROUP BY cte.idlist
I have a table with itemid|fieldid|value and i'm trying to setup a query that will combine some data and return a mathc percentage along with the result. for example, some data could be
itemid fieldid value
19 193 1
45 193 1
37 201 6
25 201 1
45 201 6
19 201 6
19 201 5
Now i want for example, to get all the rows with fieldid = 193 AND value = 1 as well as the rows with fieldid = 201 AND value = 6. The ideal result would be something like :
itemid, percentage getting 100% for all itemids which match both conditions and 50% for all that match one. I have this query working for doing the above over multiple columns but it will not work here
select id,user_class,admin, (
if (admin = 1,1,0)+
if (user_class = 'SA',1,0)
)/2*100 as the_percent
from users
WHERE
admin = 1 OR user_class = 'P'
GROUP BY id
order by the_percent DESC
Also i got the following for absolute matching
SELECT users.id FROM users WHERE
users.id IN
(
SELECT DISTINCT itemid FROM extra_field_values
INNER JOIN (SELECT DISTINCT itemid FROM extra_field_values WHERE fieldid = 201 AND value = 6 ) a1 USING (itemid)
INNER JOIN (SELECT DISTINCT itemid FROM extra_field_values WHERE fieldid = 193 AND value = 1 ) a2 USING (itemid)
)
but combining the two seems to be a bit of a puzzle to me
I think you might be able to make use of a UNION and selecting from a table subquery to make this happen. Perhaps something along the lines of:
SELECT itemid, count(*)/2*100 AS percent FROM
( SELECT itemid FROM extra_field_values WHERE fieldid = 201 AND value = 6
UNION ALL
SELECT itemid FROM extra_field_values WHERE fieldid = 193 AND value = 1 ) AS t
GROUP BY itemid;
It's been a while since I've done anything complex in mysql, and I threw this together in notepad so my syntax could be off :) But basically we create a view of the matching ids, then from that table create our statistics. (You'll also want to do some performance evaluation as well to see how it stacks up compared to doing multiple queries as well).