Group different groups as the same and count? - mysql

I have the following query:
SELECT owner,
typeOwner,
type,
count(*) AS count
FROM myTable
GROUP BY typeOwner
ORDER BY count DESC
LIMIT 0, 30
Now , the typeOwner values = '<test>a</test>' but , some times the typeOwner field will have some string else like '<test>b</test>, how can i let this query count the <test>b</test> as a group of '<test>a</test>.
I want to make an exception for this, I mean typeOwner <test>a</test> AND typeOwner <test>b</test> should be counted as one row that have two count.
here's a fiddle : http://sqlfiddle.com/#!2/70053/1 , take look
actually these are should be grouped by <type></type><aId></aId>

"actually these are should be grouped by "
You can GROUP BY something relatively ugly like:
GROUP BY LEFT(typeOwner, position('<xType>' in typeOwner) -1)
But you are probably better off preprocessing the data in some fashion. I'm not sure how MySQL handles xml, but in SQL server I might extract the XML values into first class relation fields if I needed to do this sort of processing with any frequency.
sqlfiddle.com

Based on your sqlfiddle, this works
SELECT
left(typeOwner, instr(typeOwner, '<xType>')-1) as typeOwner,
owner,type, count(*) as count
FROM `test`
GROUP BY left(typeOwner, instr(typeOwner, '<xType>')-1)
ORDER BY count DESC
LIMIT 0 , 30

Related

sql query to count records on a column which is in longtext

table :
profile_data : id(int),age(int),gender(varchar),goals(longtext)
I want to write a query which will give the average number of goals set by each of the ids.
How do I count on it when its in a textual format?
I tried :
select id,avg(count(goals)) from profile_data;
Its showing query is incorrect.
Do you perhaps require something like this?
SELECT avg(cnt)
FROM (
SELECT id
, count(goals) as cnt
FROM profile_data
GROUP BY id
) gr;
You can use GROUP BY, go through aggregate functions of your DBMS
SELECT id
,avg(goals)
FROM profile_data
GROUP BY id;

mysql get max number from a string field

I need to get maximum number from a part of the value that generally start with year followed by slash(/). So I need a maximum number after the slash(/) but year should be 2016
2016/422
2016/423
2016/469
2016/0470
2014/777
2015/123
2015/989
I tried this query
SELECT columname FROM tablename WHERE columname LIKE '2016/%' ORDER BY id DESC
the above query always giving '2016/469' as first record, how to get '2016/0470' as the maximum number?
any help will be much appreciated.
Thank you.
If columname follows that pattern YEAR/0000, you can use SUBSTRING function from MySQL to remove the part of the string you don't want.
SELECT value FROM (
SELECT CAST(SUBSTRING(columname, 0, 4) AS UNSIGNED) as year, CAST(SUBSTRING(columname FROM 6) AS UNSIGNED) as value FROM tablename
) total
ORDER BY year DESC, value DESC
LIMIT 1;
You need to split the string into 2 parts and evaluate them as numbers, instead of strings. The following formula will return the number after the / in the fieldname. All functions used below are described in the string functions section of the MySQL documentation. This way you can get the number after the / character, even if it is not year before the /, but sg else. The + 0 converts the string to a number, eliminating any leading 0.
select right(columnname, char_length(columnname)-locate('/',columnname)) + 0
from tablename
Just take the max() of the above expression to get the expected results.
UPDATE:
If you need the original number and the result has to be restricted to a specific year, then you need to join back the results to the original table:
select columnname
from tablename t1
inner join (select max(right(t.columnname, char_length(t.columnname)-locate('/',t.columnname)) + 0) as max_num
from tablename t
where left(t.columnname,4)='2016'
) t2
on right(t1.columnname, char_length(1t.columnname)-locate('/',t1.columnname)) + 0 = t2.max_num
where left(t1.columnname,4)='2016'
There are lots of suggestions given as answers already. But some of those seem overkill to me.
Seems like the only change needed to the OP query is the expression in the ORDER BY clause.
Instead of:
ORDER BY id
We just need to order by the numeric value following the slash. And there are several approaches, several expressions, that will get that from the example data.
Since the query already includes a condition columname LIKE '2016/%'
We can get the characters after the first five characters, and then convert that string to a numeric value by adding zero.
ORDER BY SUBSTRING(columname,6) + 0 DESC
If we only want to return one row, add
LIMIT 1
http://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_substring
If we only want to return the numeric value, we could use the same expression in the SELECT list, in addition columnname.
This isn't the only approach. There are lots of other approaches that will work, and don't use SUBSTRING.
Try like this:
SELECT
MAX(CAST(SUBSTRING(t.name,
LOCATE('/', t.name) + 1)
AS UNSIGNED)) AS max_value
FROM
tablename AS t;
You can try with this little uggly approach:
SELECT t.id, t2.secondNumber FROM table AS t
JOIN (SELECT id,
CONCAT(SUBSTRING(field,1,5),
if(SUBSTRING(SUBSTRING(field, 6),1,1)='0',
SUBSTRING(field, 6),
SUBSTRING(field,7)
)
) as secondNumber FROM table ) AS t2 ON t2.id=t.id
ORDER BY t2.secondNumber DESC
Would be valid only if the 0 (zeroes) before the second number (after the slash) are no more than 1.
Or if the year doesn`t matter you can try to order them only by the second number if it is ok:
SELECT t.id, t2.secondNumber FROM table AS t
JOIN (SELECT id,
if(SUBSTRING(SUBSTRING(field, 6),1,1)='0',
SUBSTRING(field, 6),
SUBSTRING(field,7)
) as secondNumber FROM table ) AS t2 ON t2.id=t.id
ORDER BY t2.secondNumber DESC

Concat 2 columns in a string, then get a count for each concatenation

I am trying to concatenate 2 columns, then count the number of rows i.e. the total number of times the merged column string exists, but I don't know if it is possible. e.g:
SELECT
CONCAT(column_1,':',column_2 ) as merged_columns,
COUNT(merged_columns)
FROM
table
GROUP BY 1
ORDER BY merged_columns DESC
Note: the colon I've inserted as a part of the string, so my result is something like 12:3. The 'count' then should tell me the number of rows that exist where column_1 =12 and column_2 = 3.
Obviously, it tells me 'merged_columns' isn't a column as it's just an alias for my CONCAT. But is this possible and if so, how?
Old question I know, but the following should work without a temp table (unless I am missing something):
SELECT
CONCAT(column_1,':',column_2 ) as merged_columns,
COUNT(CONCAT(column_1,':',column_2 ))
FROM
table
GROUP BY 1
ORDER BY merged_columns DESC
You can try creating a temp table from your concatenation select and then query that:
SELECT CONCAT(column_1,':',column_2 ) AS mergedColumns
INTO #temp
FROM table
SELECT COUNT(1) AS NumberOfRows,
mergedColumns
FROM #temp
GROUP BY mergedColumns
Hope this answer is what your are looking for.
Try this
SELECT
CONCAT(column_1,column_2 ) as merged_columns,
COUNT(*)
FROM
table
GROUP BY merged_columns
ORDER BY merged_columns DESC

MySQL sort by not sorting?

I am fetching rows from my DB using this request:
SELECT * FROM {$db_sales} WHERE date = '{$date}' ORDER BY 'amount' DESC
So, obviously, i expected the returned values to be sorted in descending order by the amount column in my DB, but it doesn't? it still fetches them, but just doesn't sort them?
Any ideas here? is my SQL statement wrong?
remove single quote around amount like this and try:
SELECT * FROM {$db_sales} WHERE date = '{$date}' ORDER BY amount DESC
Use below query
SELECT * FROM {$db_sales} WHERE date = '{$date}' ORDER BY amount DESC
ORDER BY clause uses column name.
Column name should not give in quotes.
there fore the query becomes as follows
SELECT * FROM {$db_sales} WHERE date = '{$date}' ORDER BY amount DESC

How to fetch the first and last record of a grouped record in a MySQL query with aggregate functions?

I am trying to fetch the first and the last record of a 'grouped' record.
More precisely, I am doing a query like this
SELECT MIN(low_price), MAX(high_price), open, close
FROM symbols
WHERE date BETWEEN(.. ..)
GROUP BY YEARWEEK(date)
but I'd like to get the first and the last record of the group. It could by done by doing tons of requests but I have a quite large table.
Is there a (low processing time if possible) way to do this with MySQL?
You want to use GROUP_CONCAT and SUBSTRING_INDEX:
SUBSTRING_INDEX( GROUP_CONCAT(CAST(open AS CHAR) ORDER BY datetime), ',', 1 ) AS open
SUBSTRING_INDEX( GROUP_CONCAT(CAST(close AS CHAR) ORDER BY datetime DESC), ',', 1 ) AS close
This avoids expensive sub queries and I find it generally more efficient for this particular problem.
Check out the manual pages for both functions to understand their arguments, or visit this article which includes an example of how to do timeframe conversion in MySQL for more explanations.
Try This to start with... :
Select YearWeek, Date, Min(Low_Price), Max(High_Price)
From
(Select YEARWEEK(date) YearWeek, Date, LowPrice, High_Price
From Symbols S
Where Date BETWEEN(.. ..)
GROUP BY YEARWEEK(date)) Z
Group By YearWeek, Date
Here is a great specific solution to this specific problem:
http://topwebguy.com/first-and-last-in-mysql-a-working-solution/
It's almost as simple as using FIRST and LAST in MySQL.
I will include the code that actually provides the solution but you can look upi the whole text:
SELECT
word ,
(SELECT a.ip_addr FROM article a
WHERE a.word = article.word
ORDER BY a.updated LIMIT 1) AS first_ip,
(SELECT a.ip_addr FROM article a
WHERE a.word = article.word
ORDER BY a.updated DESC LIMIT 1) AS last_ip
FROM notfound GROUP BY word;
Assuming that you want the ids of the records with the lowest low_price and the highest high_price you could add these two columns to your query,
SELECT
(SELECT id ORDER BY low_price ASC LIMIT 1) low_price_id,
(SELECT id ORDER BY high_price DESC LIMIT 1) high_price_id,
MIN(low_price), MAX(high_price), open, close
FROM symbols
WHERE date BETWEEN(.. ..)
GROUP BY YEARWEEK(date)
If efficiency is an issue you should add a column for 'year_week', add some covering indexes, and split the query in two.
The 'year_week' column is just an INT set to the value of YEARWEEK(date) and updated whenever the 'date' column is updated. This way you don't have to recalculate it for each query and you can index it.
The new covering indexes should look like this. The ordering is important.
KEY yw_lp_id (year_week, low_price, id),
KEY yw_hp_id (year_week, high_price, id)
You should then use these two queries
SELECT
(SELECT id ORDER BY low_price ASC LIMIT 1) low_price_id,
MIN(low_price), open, close
FROM symbols
WHERE year_week BETWEEN(.. ..)
GROUP BY year_week
and
SELECT
(SELECT id ORDER BY high_price DESC LIMIT 1) high_price_id,
MAX(high_price), open, close
FROM symbols
WHERE year_week BETWEEN(.. ..)
GROUP BY year_week
Covering indexes are pretty useful. Check this out for more details.