SQL/Sybase query group by a substring of a column value - mysql

I am making a simple select query with the following result -
select source_uri from image
--
source_uri
"image/30022/A.jpg"
"image/30022/B.jpg"
"image/30022/C.jpg"
"image/30022/D.jpg"
"image/30023/A.jpg"
"image/30023/B.jpg"
"image/30023/C.jpg"
"image/30023/D.jpg"
"image/30024/A.jpg"
"image/30024/B.jpg"
"image/30024/C.jpg"
"image/30024/D.jpg"
I want result like -
source_uri
"image/30022/A.jpg"
"image/30023/B.jpg"
"image/30024/C.jpg"
I tried having a group by clause... but it doesnot group by since the values are different.
--
I am able to use the SUBSTRING_INDEX function in mySQL.
Looking for the exact alternative in Sybase.

If there is always the last 6 i.e D.jpg" characters you need to apply group by then you can use the RIGHT(col,len),Right returns the rightmost len characters from the string str, or NULL if any argument is NULL
select source_uri
from image
GROUP BY RIGHT(source_uri ,6)
RIGHT(str,len)
Other way you need get the part after last / so use SUBSTRING_INDEX
select source_uri
from image
GROUP BY SUBSTRING_INDEX(source_uri ,'/',-1)
SUBSTRING_INDEX(str,delim,count)
EDIT after reading comments
SUBSTRING_INDEX(source_uri ,'/',-1) /* will give you A.jpg*/
Now to get remaining part you can do so
SUBSTRING_INDEX(source_uri ,SUBSTRING_INDEX(source_uri ,'/',-1) ,1) /* will give you image/30022/ */

FOR MySQL -
M Khalid Junaid's answer works great.
FOR Sybase -
I did a lean trick.. not sure how efficient this is -
select source_uri from image
Problem: To extract "image/30022" from "image/30022/a.jpg"
Solution:
1) Got the file length - file_length = CHARINDEX('/', reverse(source_uri))
2) Substring - SUBSTRING(source_uri, 1, LEN(source_uri) - file_length)
select SUBSTRING(source_uri, 1, LEN(source_uri) - file_length) from image
group by SUBSTRING(source_uri, 1, LEN(source_uri) - file_length)

Related

Why is my query returning "OK" instead of rows?

I have the following query:
SELECT
(sign(mr.p1_h2h_win_one_time - mr.p2_h2h_win_one_time)) AS h2h_win_one_time_1,
(abs(mr.p1_h2h_win_one_time - mr.p2_h2h_win_one_time) ^ 2) AS h2h_win_one_time_2
FROM belgarath.match_result AS mr
LIMIT 10
Which returns:
However, when I try to multiply the two fields:
SELECT
(
sign(mr.p1_h2h_win_one_time - mr.p2_h2h_win_one_time)
) *
(
abs(mr.p1_h2h_win_one_time - mr.p2_h2h_win_one_time) ^ 2
) AS h2h_win_one_time_comb
FROM belgarath.match_result AS mr
LIMIT 10
Workbench simply returns OK instead of any rows.
Doing some investigation I can get the first two rows to display if I use LIMIT 2. Looking at the returned values above I guess there must be some issue with multiplying the minus values or zero values from rows 3-10. However, this can be done simply on a calculator so what am I missing?
Maybe you think that the operator ^ is the power operator when in fact it is the Bitwise XOR operator.
MySql has the function pow() for your case:
pow(abs(mr.p1_h2h_win_one_time - mr.p2_h2h_win_one_time), 2)

MySQL sorting with alphanumeric prefix

I've got a database with a column that contains the following data:
aaa-1
aaa-2
aaa-3
...
aaa-10
aaa-11
...
aaa-100
aaa-101
...
aaa-1000
When I query and sort the data in ascending order, I get:
aaa-1
aaa-10
aaa-11
...
aaa-100
aaa-101
...
aaa-1000
...
aaa-2
...
aaa-3
Is this actually the correct (machine) way of sorting? Is the order being screwed up because of the aaa- prefix? How do I go about sorting this the way a human would (ie something that looks like the first snippet)?
P.S. If the problem does lie in the prefix, is there a way to remove it and sort with just the numeric component?
P.P.S. It's been suggested to me that I should just change my data and add leading zeroes like aaa-0001 and aaa-0002, etc. However, I'm loathe to go that method as each time the list goes up an order of 10, I'd have to reformat this column.
Thank you all in advance! :)
You can extract the number part, convert it to numeric data type and then do an ORDER BY:
SELECT mytable.*,
CAST(SUBSTRING_INDEX(mycolumn, '-', - 1) AS UNSIGNED) mycolumnintdata
FROM
mytable
ORDER BY mycolumnintdata;
If there are expressions which does not match number, the CAST function would return 0 and those records would be displayed first. You may handle this separately if needed.
I had a similar issue and the trick that did it for me was this one
*"ORDER BY LENGTH(column_name), column_name
As long as the non-numeric part of the value is the same length, this will sort 1 before 10, 10 before 100, etc."*
as given by Andreas Bergström on this question.
Hope that helps someone.
this is the alphabetical order,
you want numerical order,
for do this you must in the ORDER BY clause
trim the costant "aaa-" part
convert it in number
convert(SUBSTRING(val, 3), integer)
I will give you a sample sorting. Not based on your data sample, but this could help you out.
Say you have data like this :
id
----
1
2
6
10
13
when you do ORDER BY id ASC would return :
id
----
1
10
13
2
6
I suggest, use LPAD.
This query : SELECT LPAD('12',5,'0') return 00012
So when you have table data like I provide above, you can sort them like this :
SELECT * FROM TABLE
ORDER BY LPAD(ID,7,'0') ASC
Based on your data.
SELECT SUBSTR('aaa-100',5,LENGTH('aaa-100') - 3) return 100
So, SELECT LPAD( SUBSTR('aaa-100',5,LENGTH('aaa-100') - 3), 7, '0') return 00000100
So you can combine string function such as SUBSTR and LPAD. Do have any clue now?

Order a VARCHAR column numerically

Current SQL: SELECT code FROM myTable ORDER BY code ASC
code
---
11
113
12
13A
This is the current order I have of a MySQL table.
I want the order to be A-Z, 1-10 however, numerically, like this:
code
---
11
12
13A
113
The reason I cannot achieve this effect in the first place is because the code column is varchar and not int. However as shown in the example, some codes have a letter prepended to them so I cannot change this to integer.
How can I get around this problem without changing the data type?
The simplest way is to use silent conversion. Just add 0:
order by code + 0
In practice, you might want:
order by code + 0, code
This should work, sorting by number first and alpha after (if the same number):
select * from myTable
order by cast(replace(code,'[0-9]+','') as unsigned), code
See this SQL Fiddle

Select data which have same letters

I'm having trouble with this SQL:
$sql = mysql_query("SELECT $menucompare ,
(COUNT($menucompare ) * 100 / (SELECT COUNT( $menucompare )
FROM data WHERE $ww = $button )) AS percentday FROM data WHERE $ww >0 ");
$menucompare is table fields names what ever field is selected and contains data bellow
$button is the week number selected (lets say week '6')
$ww table field name with row who have the number of week '6'
For example, I have data in $menucompare like that:
123456bool
521478bool
122555heel
147788itoo
and I want to select those, who have same word in the last of the data and make percentage.
The output should be like that:
bool -- 50% (2 entries)
heel -- 25% (1 entry)
itoo -- 25% (1 entry)
Any clearness to my SQL will be very appreciated.
I didn't find anything like that around.
Well, keeping data in such format probably not the best way, if possible, split the field into 2 separate ones.
First, you need to extract the string part from the end of the field.
if the length of the string / numeric parts is fixed, then it's quite easy;
if not, you should use regular expressions which, unfortunately, are not there by default with MySQL. There's a solution, check this question: How to do a regular expression replace in MySQL?
I'll assume, that numeric part is fixed:
SELECT s.str, CAST(count(s.str) AS decimal) / t.cnt * 100 AS pct
FROM (SELECT substr(entry, 7) AS str FROM data) AS s
JOIN (SELECT count(*) AS cnt FROM data) AS t ON 1=1
GROUP BY s.str, t.cnt;
If you'll have regexp_replace function, then substr(entry, 7) should be replaced to regexp_replace(entry, '^[0-9]*', '') to achieve the required result.
Variant with substr can be tested here.
When sorting out problems like this, I would do it in two steps:
Sort out the SQL independently of the presentation language (PHP?).
Sort out the parameterization of the query and the presentation of the results after you know you've got the correct query.
Since this question is tagged 'SQL', I'm only going to address the first question.
The first step is to unclutter the query:
SELECT menucompare,
(COUNT(menucompare) * 100 / (SELECT COUNT(menucompare) FROM data WHERE ww = 6))
AS percentday
FROM data
WHERE ww > 0;
This removes the $ signs from most of the variable bits, and substitutes 6 for the button value. That makes it a bit easier to understand.
Your desired output seems to need the last four characters of the string held in menucompare for grouping and counting purposes.
The data to be aggregated would be selected by:
SELECT SUBSTR(MenuCompare, -4) AS Last4
FROM Data
WHERE ww = 6
The divisor in the percentage is the count of such rows, but the sub-stringing isn't necessary to count them, so we can write:
SELECT COUNT(*) FROM Data WHERE ww = 6
This is exactly what you have anyway.
The divdend in the percentage will be the group count of each substring.
SELECT Last4, COUNT(Last4) * 100.0 / (SELECT COUNT(*) FROM Data WHERE ww = 6)
FROM (SELECT SUBSTR(MenuCompare, -4) AS Last4
FROM Data
WHERE ww = 6
) AS Week6
GROUP BY Last4
ORDER BY Last4;
When you've demonstrated that this works, you can re-parameterize the query and deal with the presentation of the results.

MySQL: I need to get the offset of a item in a query

Mysql: i need to get the offset of a item in a query.
I have a image gallery: this show 6 image per stack, so when i request image 22 it shows images from 18 to 24. It should first get the offset of the image 22, then get the images from 18 to 24.
Another example: i request the image number 62(and offset 62), it will select images with offset from 60 to 66.
Is possible with a single query?
The main important thing is to get the offset value of the item that has its id equal to a number.
Thanks ;)
EDIT:
select * from images order_by updated_at offset(here i need to get the offset of the image id in this query, and the make some calculation... this is what i need, if is possible.. :d)
EDIT2:
Now I understand that I need 2 queries:
1º: get the offset of the image within the query with my custom order
2º: get the images using the offset from the first query... this I can make it alone, the first one is the problem.. :s
If your images have sequential IDs, you may want to do the following:
SELECT *
FROM images
WHERE id >= ((? DIV 6) * 6) AND
id < (((? DIV 6) + 1) * 6)
ORDER BY id;
Replace the ? parameter in the above query with the ID of the image requested.
UPDATE: It seems that your images are not ordered by a sequential ID, but by a timestamp. Unfortunately it looks like MySQL does not support variable expressions in the LIMIT clause (Source). One option would be to use a prepared statement:
PREPARE stmt FROM
" SELECT *
FROM images
ORDER BY updated_at
LIMIT ?, 6";
SET #lower_limit := ((22 DIV 6) * 6);
EXECUTE stmt USING #lower_limit;
Another option could be:
SET #row = 0;
SELECT *
FROM images
WHERE (#row := #row + 1) BETWEEN ((3 DIV 6) * 6) + 1 and (((3 DIV 6) + 1) * 6)
ORDER BY updated_at;
EDIT: It appears MySQL does not allow this kind of expression in the OFFSET clause. If you can use a prepared statement (either directly in SQL or in another language), you can make the calculation beforehand and use it. See Daniel's answer for that. I'm leaving this answer here because of the other useful information.
What you are looking for is called pagination. In MySQL you can do it with the LIMIT and OFFSET keywords:
SELECT image
FROM images
ORDER BY updated_at
LIMIT 6
OFFSET (DIV(?, 6) * 6)
Replace ? with the requested image index. Note that this will give images with offsets 60, 61, 62, 63, 64, 65 when you ask for image 62. I assumed the last offset you gave in the examples was exclusive. You should adjust accordingly if I made the wrong assumption.
And a little explanation:
LIMIT makes the query return only the given number of results. Because you always want six, that makes it easy.
OFFSET makes the query return only results from the given offset. The calculation does integer division by six and multiplies by six. This results in the previous multiple of six of the given number, exactly what you want.
I may not understand you, but why do you have to do everything in MySQL?
Let's assume you use LAMP:
$pagination_start = (int)(floor($id_requested / 6)*6);
$offset_array = ($id_requested % 6);
$offset_mysql = $pagination_start + $offset_array;
Now you have start of your pagination in $pagination_start [i.e. 60] and requested image offset [i.e. 62] in $offset_mysql:
SELECT image
FROM images
ORDER BY updated_at DESC
LIMIT $pagination_start, 6
Now in return, you get an array cotaning 6 images and the one requested is at $result[$offset_array].
SELECT image
FROM images
ORDER BY updated_at
LIMIT 6, FLOOR($number / 6 ) * 6;