Is it possible get data with search data between values - mysql

I am having data in table USER_TABLE as following :
+-----------------+
| USERS_RANGE |
+-----------------+
| 5-98 |
| 9854-98666620 |
| 54-986 |
| 1-20 |
| 10000-122222220 |
| 10-1222 |
+-----------------+
My requirement hear:
Ex:- if i search "7" it should display as :
+-------------+
| USERS_RANGE |
+-------------+
| 5-98 |
| 1-20 |
+-------------+
I tried
ex:- select * from USER_TABLE WHERE USERS_RANGE <= '7';
and some more mathamatical things in java
Is there any query to get data like this.

We can get the required data using subquery approach and SUBSTRING_INDEX() function.
/* select all inside subquery and filter with WHERE*/
SELECT USERS_RANGE FROM (
/* select all range and split the range between '-' */
SELECT
USERS_RANGE
, SUBSTRING_INDEX(USERS_RANGE, '-', 1) AS `from`
, SUBSTRING_INDEX(USERS_RANGE, '-', -1) AS `to`
FROM USERS_RANGE_TABLE
) A
WHERE A.from <= 7 AND A.to >= 7;
The subquery will select all USER_RANGE data, split the range and create a new column with alias from and to,
and the main query will select all inside subquery and filter the result with where by column from and to.
Hope this help answer your question.
Reference:
https://dev.mysql.com/doc/refman/8.0/en/string-functions.html#function_substring-index
https://dev.mysql.com/doc/refman/8.0/en/subqueries.html

Related

How to loop through an array in SQL where clause?

I have a MySQL table which has the following columns and records:
| Name | Total | GivenBy |
| ---- | -------- | ------------ |
| Z | 200 |['A','B','C'] |
| X | 240 |['A','D','C'] |
I would like to extract Record No. 1 on the basis of 3rd column where the SQL query would be like:
SELECT * FROM mytable WHERE GivenBy='B';
Is there a way I can loop through the list in third column and take out the respective string as required in the SQL WHERE clause in a single query?
Please note that I cannot add more columns in the table.
If you can please provide the query as MySQL compatible, I would really appreciate it.
The "array" you show isn't quite valid JSON, but if you use double-quotes instead of single-quotes, you can use JSON_TABLE() to do this:
CREATE TABLE MyTable
(
Name CHAR(1) PRIMARY KEY,
Total INT NOT NULL,
GivenBy JSON NOT NULL
);
INSERT INTO MyTable VALUES
('Z', 200, '["A","B","C"]'),
('X', 240, '["A","D","C"]');
SELECT Name, Total, g.Value
FROM MyTable
CROSS JOIN JSON_TABLE(GivenBy, '$[*]' COLUMNS(Value CHAR(1) PATH '$')) AS g;
+------+-------+-------+
| name | total | value |
+------+-------+-------+
| X | 240 | A |
| X | 240 | D |
| X | 240 | C |
| Z | 200 | A |
| Z | 200 | B |
| Z | 200 | C |
+------+-------+-------+
But the best choice is not to store "arrays" in MySQL. Store the values one per row in a second table.
You can use the "like" keyword with regex to match your requirements in the third column.
select * from table where givenBy like "%B%";
Something similar would work.
You need to run a script:
Retrieve the list of unique values in the GivenBy column using the following query:
SELECT DISTINCT JSON_EXTRACT(GivenBy, '$[*]') AS GivenByValues
FROM mytable;
Loop through the list of unique values, and for each value, run a query that uses that value in the WHERE clause:
SELECT *
FROM mytable
WHERE JSON_SEARCH(GivenBy, 'one', [current_value_from_loop]) IS NOT NULL;

How to select both sum value of all rows and values in some specific rows?

I have a record table and its comment table, like:
| commentId | relatedRecordId | isRead |
|-----------+-----------------+--------|
| 1 | 1 | TRUE |
| 2 | 1 | FALSE |
| 3 | 1 | FALSE |
Now I want to select newCommentCount and allCommentCount as a server response to the browser. Is there any way to select these two fields in one SQL?
I've tried this:
SELECT `isRead`, count(*) AS cnt FROM comment WHERE relatedRecordId=1 GROUP BY `isRead`
| isRead | cnt |
| FALSE | 2 |
| TRUE | 1 |
But, I have to use a special data structure to map it and sum the cnt fields in two rows to get allCommentCount by using an upper-layer programming language. I want to know if I could get the following format of data by SQL only and in one step:
| newCommentCount | allCommentCount |
|-----------------+-----------------|
| 2 | 3 |
I don't even know how to describe the question. So I got no any search result in Google and Stackoverflow. (Because of My poor English, maybe)
Use conditional aggregation:
SELECT SUM(NOT isRead) AS newCommentCount, COUNT(*) AS allCommentCount
FROM comment
WHERE relatedRecordId = 1;
if I under stand you want show sum of newComments Count and all comments so you can do it like
SELECT SUM ( CASE WHEN isRead=false THEN 1 ELSE 0 END ) AS newComment,
Count(*) AS AllComments From comments where relatedRecord=1
also you can make store procedure for it.
To place two result sets horizontally, you can as simple as use a subquery for an expression in the SELECT CLAUSE as long as the number of rows from the result sets match:
select (select count(*) from c_table where isread=false and relatedRecordId=1 ) as newCommentCount,
count(*) as allCommentCount
from c_table where relatedRecordId=1;

Group rows by the same value in the field, while matching on partial value only

I have a table that has many rows (between a few 1000s to a few million).
I need my query to do the following:
group results by the same part of the value in the field;
order by the biggest group first.
The table has mostly values that have only some part are similar (and i.e. suffix would be different). Since the number of similar values is huge - I cannot predict all of them.
Here is i.e. my table:
+--------+-----------+------+
| Id | Uri | Run |
+--------+-----------+------+
| 15145 | select_123| Y |
| 15146 | select_345| Y |
| 15148 | delete_123| N |
| 15150 | select_234| Y |
| 15314 | delete_334| N |
| 15315 | copy_all | N |
| 15316 | merge_all | Y |
| 15317 | select_565| Y |
| 15318 | copy_all | Y |
| 15319 | delete_345| Y |
+--------+-----------+------+
What I would like to see, something like this (the Count part is desirable but not required):
+-----------+------+
| Uri | Count|
+-----------+------+
| select | 4 |
| delete | 3 |
| copy_all | 2 |
| merge_all| 1 |
+-----------+------+
If you're using MySQL 5.x, you can strip the trailing _ and digits from the Uri value using this expression:
LEFT(Uri, LENGTH(Uri) - LOCATE('_', REVERSE(Uri)))
Using a REGEXP test to see if the Uri ends in _ and some digits, we can then process the Uri according to that and then GROUP BY that value to get the counts:
SELECT CASE WHEN Uri REGEXP '_[0-9]+$' THEN LEFT(Uri, LENGTH(Uri) - LOCATE('_', REVERSE(Uri)))
ELSE Uri
END AS Uri2,
COUNT(*) AS Count
FROM data
GROUP BY Uri2
Output:
Uri2 Count
copy_all 2
delete 3
merge_all 1
select 4
Demo on SQLFiddle
The format of the string makes it uneasy to parse it with string functions.
If you are running MySQL 8.0, you can truncate the string with regexp_replace(), then group by and order by:
select regexp_replace(uri, '_\\d+$', '') new_uri, count(*) cnt
from mytable
group by new_uri
order by cnt desc
If you're using MySQL 8.x, you can use REGEXP_REPLACE() to remove the numeric suffixes from select_XXX and delete_XXX, then group by the result.
SELECT REGEXP_REPLACE(uri, '_[0-9]+$', '') AS new_uri, COUNT(*) as count
FROM yourTable
GROUP BY new_uri
You can do as below and create a view and using the case expression + substr find which are 'select' and 'delete'.
Following the view you can query it with the count/group_by.
WITH view_1 AS (
SELECT
CASE
WHEN substr(uri, 1, 6) = 'select' THEN
substr(uri, 1, 6)
WHEN substr(uri, 1, 6) = 'delete' THEN
substr(uri, 1, 6)
ELSE uri
END AS uri
FROM
your_table
)
SELECT
uri,
COUNT(uri) as "Count"
FROM
view_1
GROUP BY
uri
ORDER BY count(uri) DESC;
Output will be
delete 5
merge_all 4
select 3
copy_all 3

How to check missing timestamp in MySql

In my SQL database (MySql), I want to record the price history of an asset.
I have a table with a timestamp as a primary key and price as the value. It has only two column timestamp / price
There should be one price point per second recorded.
Sometimes, there are missing price points. (When the server goes down)
Here is an example of the timestamp column.
**timestamp**
1581431400
1581431401
1581431402
1581431403
1581431405
1581431406 //missing 4 rows price points after this
1581431410
1581431411
1581431412
1581431413
1581431414
1581431415 //missing 3 rows price points after this
1581431418
1581431419
1581431420
Given two timestamps, how to run a SQL query that will fetch the timestamp ranges where the data exists without querying the entire database?
For example, I let's say the two timestamp in UNIX are 1 and 2000000000
What is the SQL query I should run to return the following ranges:
[
[1581431400,1581431406],
[1581431410,1581431415],
[1581431418,1581431420]
]
Here is my answer (Hack). You can use a query like this.
SELECT CONCAT( '[',GROUP_CONCAT('\n',
'[', res.missing_from, '],'
,'[', res.missing_to -1,']') , '\n]') AS missing
FROM (
SELECT m.ts+1 AS missing_from,
(SELECT ts FROM mytable WHERE ts > m.ts ORDER BY ts LIMIT 1 ) as missing_to
FROM mytable m
LEFT JOIN mytable mf ON m.ts+1 = mf.ts
WHERE
mf.ts IS NULL
) AS res
WHERE res.missing_to - res.missing_from > 0;
SAMPLE
mysql> SELECT * FROM mytable;
+------------+
| ts |
+------------+
| 1581431400 |
| 1581431401 |
| 1581431402 |
| 1581431403 |
| 1581431405 |
| 1581431406 |
| 1581431410 |
| 1581431411 |
| 1581431412 |
| 1581431413 |
| 1581431414 |
| 1581431415 |
| 1581431418 |
| 1581431419 |
| 1581431420 |
+------------+
15 rows in set (0.00 sec)
TEST
mysql> SELECT CONCAT( '[',GROUP_CONCAT('\n',
'[', res.missing_from, '],'
,'[', res.missing_to -1,']') , '\n]') AS missing
FROM (
SELECT m.ts+1 AS missing_from,
(SELECT ts FROM mytable WHERE ts > m.ts ORDER BY ts LIMIT 1 ) as missing_to
FROM mytable m
LEFT JOIN mytable mf ON m.ts+1 = mf.ts
WHERE
mf.ts IS NULL
) AS res
WHERE res.missing_to - res.missing_from > 0;
+-------------------------------------------------------------------------------------+
| missing |
+-------------------------------------------------------------------------------------+
| [
[1581431404],[1581431404],
[1581431407],[1581431409],
[1581431416],[1581431417]
] |
+-------------------------------------------------------------------------------------+
1 row in set (0.01 sec)
I would simply use window functions:
select min(timestamp), max(timestamp)
from (select timestamp, row_number() over (order by timestamp) as seqnum
from t
) t
group by (timestamp - seqnum);
I'm not sure what "without querying the entire database?" is supposed to mean. This reads the table -- as any such query would need to -- but does not need to query anything else in the database.
This illustrates what happens:
timestamp seqnum diff
1581431400 1 1581431399
1581431401 2 1581431399
1581431402 3 1581431399
1581431403 4 1581431399
1581431405 5 1581431400
1581431406 6 1581431400
1581431410 7 1581431403
1581431411 8 1581431403
The last column is identifying adjacent timestamps that differ by "1". That is what is aggregated in the outer query.

How to run alternative where condition if row is zero

Suppose i have a table
table
+-------------+
| id | name |
+-------------+
| 1 | xabx |
| 2 | abxd |
| 3 | axcd |
| 4 | azyx |
| 5 | atyl |
| 6 | aksd |
| 7 | baabc|
| 8 | aabcd|
+-------------+
first i have to get data if matches first some char like :
if name = aab
then have to run select * from table where name like 'aab%'
then it returns
+-------------+
| 8 | aabcd |
+-------------+
which execatlly i want
but if i have only abc
then the above query return 0 row
then i have to search from middle like :
select * from table where name like '%abc%'
then it returns which is the alternative
+-------------+
| 7 | baabc|
| 8 | aabcd|
+-------------+
i have no much knowledge about mysql is there any query which can do like if first where condition don't have row then run alternative where condition
i have tried this but didn't work as i want.
select * from table where name like 'abc%' or name like '%abc%'
fiddle
thanks in advance
This is somewhat your desired result:
select *from t
where (case
when name like 'abc' then 1
when name like 'bc%' then 1
when name like '%bc' then 1
when name like '%bc%' then 1
else null
end)
order by name
limit 1;
I just put all the combinations as conditions.
You can interchange their sequence or remove unnecessary condition.
limit 1 makes only 1 row visible for whichever condition satisfies.
Here is the answer from your fiddle. Check it out
Hope it helps!
This is a possible solution:
Left joining the table on itself, where the table is initially filtered by the more inclusive %bx% and then the join is filtered by the more restrictive bx%.
This allows you to use the joined name if it exists, but revert to the original if not:
SELECT t1.id, IF(t2.name IS NULL, t1.name, t2.name) name
FROM test t1
LEFT JOIN test t2 ON t2.id = t1.id AND t1.name like 'bx%'
WHERE t1.name LIKE '%bx%'
This may/may not be ideal depending on the size or your dataset.
COUNT checking may work
select *
from table
where name like 'aab%' or
((select count(*) from table where name like 'aab%') = 0 and name like '%abc%')
I guess that it would be a good idea to compute the count value into a variable first, however, the optimizer may recognize independent subquery anyway and run it once.