How to inner join result of stored function? - mysql

I am searching for all day with no success so I decided to ask.
I will very simplify structure as much as possible to ask for essence.
I have function:
mysql> SELECT set_of_ids_to_names('1:2:3:4:5', ':') AS `res`;
+-------------------------------+
| res |
+-------------------------------+
| NameA:NameB:NameC:NameD:NameE |
+-------------------------------+
I have table:
mysql> SELECT * FROM `tbl_tool`;
+----+-----------------+---------+
| ID | Tool | ID_name |
+----+-----------------+---------+
| 1 | Tool_1 | 1:2:3:4 |
| 2 | Tool_2 | 2:4:5 |
| 3 | Tool_3 | 4:5 |
| 4 | Tool_4 | 3 |
+----+-----------------+---------+
The result I would like to achieve is to have view called 'v_tool' so once I selet it I get:
mysql> SELECT * FROM `v_tool`;
+----+-----------------+-------------------------+
| ID | Tool | Name |
+----+-----------------+-------------------------+
| 1 | Tool_1 | NameA:NameB:NameC:NameD |
| 2 | Tool_2 | NameB:NameD:NameE |
| 3 | Tool_3 | NameD:NameE |
| 4 | Tool_4 | NameC |
+----+-----------------+-------------------------+
This what I tried is:
SELECT `tbl_tool`.`ID`, `tbl_tool`.`Tool`, `Name` FROM `tbl_tool`
INNER JOIN (SELECT set_of_ids_to_names((SELECT `ID` FROM `tbl_tool` WHERE `ID` = `tbl_tool`.`ID`), ':') AS `Name`) AS `aaa`
I know that it is wrong, but I just could not find idea how to pass proper value to function 'set_of_ids_to_names'.
Big thank you in advance.

Looking at the original function call you made:
SELECT set_of_ids_to_names('1:2:3:4:5', ':') AS `res`
It is important to note the function call appears in the SELECT clause, not in the FROM clause.
This suggests set_of_ids_to_names is a scalar function, not a table-valued function.
When querying table tbl_tool, you can do the exact same thing: call set_of_ids_to_names in the SELECT clause.
SELECT Tool, set_of_ids_to_names(ID_name, ':') AS Name
FROM tbl_tool
For table-valued functions, the situation is different of course. SQL Server has CROSS APPLY for that, in MySQL you'd probably have to join the table with a subquery encapsulating the function call.

Related

Select value from table sorted by a certain order from another table

I want to select value from table sorted by a certain order.
I have a table called test that looks like this:
| date | code | value |
+----------+-----------+----------+
| 20050104 | 000005.SZ | -6359.19 |
| 20050104 | 600601.SH | -7876.34 |
| 20050104 | 600602.SH | -25693.3 |
| 20050104 | 600651.SH | NULL |
| 20050104 | 600652.SH | -15309.9 |
...
| 20050105 | 000005.SZ | -4276.28 |
| 20050105 | 600601.SH | -3214.56 |
...
| 20170405 | 000005.SZ | 23978.13 |
| 20170405 | 600601.SH | 32212.54 |
Right now I want to select only one date, say date = 20050104, and then sort the data by a certain order (the order that each stock was listed in the stock market).
I have another table called stock_code which stores the correct order:
+---------+-----------+
| code_id | code |
+---------+-----------+
| 1 | 000002.SZ |
| 2 | 000004.SZ |
| 3 | 600656.SH |
| 4 | 600651.SH |
| 5 | 600652.SH |
| 6 | 600653.SH |
| 7 | 600654.SH |
| 8 | 600602.SH |
| 9 | 600601.SH |
| 10 | 000005.SZ |
...
I want to sorted the selected data by stock_code(code_id), but I don't want to use join because it takes too much time. Any thoughts?
I tried to use field but it gives me an error, please tell me how to correct it or give me an even better idea.
select * from test
where date = 20050104 and code in (select code from stock_code order by code)
order by field(code, (select code from stock_code order by code));
Error Code: 1242. Subquery returns more than 1 row
You told us that you don't want to join because it takes too much time, but the following join query is probably the best option here:
SELECT t.*
FROM test t
INNER JOIN stock_code sc
ON t.code = sc.code
WHERE t.date = '20050104'
ORDER BY sc.code_id
If this really runs slowly, then you should check to make sure you have indices setup on the appropriate columns. In this case, indices on the code columns from both tables as well as an index on test.date should be very helpful.
ALTER TABLE test ADD INDEX code_idx (code)
ALTER TABLE test ADD INDEX date_idx (date)
ALTER TABLE code ADD INDEX code_idx (code)

NOT IN not working in mysql procedure

I have this table that represents friendships
+--------+--------+
| user_1 | user_2 |
+--------+--------+
| 1 | 5 |
| 2 | 67 |
| 3 | 23 |
| ... | ... |
+--------+--------+
My goal is to create a procedure that returns friends of friends of a user (that does not include friends).
I started by creating a procedure to return friends for a given user
CREATE DEFINER=`user`#`localhost` PROCEDURE `getFriends`(IN `myuser` BIGINT(20))
NO SQL
BEGIN
DROP TABLE IF EXISTS friends;
CREATE TEMPORARY TABLE friends
SELECT user_2
FROM fb_friends
WHERE user_1=myuser;
END
This procedure, for user 534477793, creates the following temporary table
+------------+
| user_2 |
+------------+
| 527419864 |
| 580101923 |
| 620972114 |
| 651861323 |
| 662123645 |
| 676185145 |
| 682866129 |
| 718761310 |
| 729611272 |
| 1036862839 |
+------------+
Then I created another procedure that calls the first one and return friends of friends
CREATE DEFINER=`user`#`localhost` PROCEDURE `getFriendsOfFriends`(IN `myuser` BIGINT(20))
BEGIN
-- Creates the table friends
CALL getFriends(myuser);
SELECT DISTINCT(fb.user_2)
FROM fb_friends fb, friends f
-- This works
WHERE fb.user_1 IN (f.user_2)
-- This doesn't
AND fb.user_2 NOT IN (f.user_2);
END
And the query returns the following:
+------------+
| user_2 |
+------------+
| 729611272 |
| 527419864 |
| 651861323 |
| 676185145 |
| 1036862839 |
| 502741322 |
| 546744626 |
| 636845886 |
| 652813833 |
| 663713246 |
| 682866129 |
| 781419583 |
| 845134109 |
| 1355751897 |
| 1359286892 |
| 1275961636 |
| 620972114 |
| 509609160 |
| 662123645 |
| 1460283586 |
+------------+
So it's clear that the NOT IN didn't work since all values from getFriends are in the second results set.
I managed to get the results I wanted by doing ugly stuff but still, I'd like to understand what's wrong here. And there's no NULL value anywhere by the way.
Thanks!
That's because f.user_2 in your NOT IN is just a single user, not the entire set.
The correct way to do it is
SELECT DISTINCT(fb.user_2)
FROM fb_friends fb, friends f
WHERE fb.user_1 IN (f.user_2)
AND fb.user_2 NOT IN (SELECT * FROM friends);
Note the the first IN can be replaced with an = operator, because again there's only one element in the IN list.
Or you can completely remove the join to make the query consistent (but it will probably regress the performance)
SELECT DISTINCT(fb.user_2)
FROM fb_friends fb
WHERE fb.user_1 IN (SELECT * FROM friends)
AND fb.user_2 NOT IN (SELECT * FROM friends);
I thought the problem might be with temporary table and I was right. By executing the query in MySQLWorkbench the following error was ouput:
Error Code: 1137. Can't reopen table: 'friends'
So I went online to check if there was a limit on the number of times a temporary table can be used in the same query and in fact there is.
So I might just the temporary table into a permanent one (and drop it every time beforehand) and all will work as expected. sigh

merger one row with null values to not null values of another row mysql

I want to merge two rows into one.The below format is in the database.
+----+---------+-----------------------+-------------------------+
| id | appid | photo | signature |
+====+=========+=======================+=========================+
| 1 | 10001 | 10001.photograph.jpg | NULL |
| 2 | 10001 | NULL | 10001.signature.jpg |
+----+---------+-----------------------+-------------------------+
I want a mysql query so that i can fetch data like below,
+--------+------------------------+-------------------------+
| appid | photo | signature |
+========+========================+=========================+
|10001 | 10001.photograph.jpg | 10001.signature.jpg |
+--------+------------------------+-------------------------+
Kindly suggest...
You can also use max function
select appid,
max(photo) photo,
max(signature) signature
from test
group by appid
Demo
This should do this:
select t1.appid,t1.photo,t2.signature from mytable t1 join mytable t2 on t1.appid=t2.appid where t1.id=1 and t2.id=2

SQL select statement optimizing (id, parent_id, child_ids)

we have a very old custom db (oracle, mysql, derby) with the restrictions: no new table fileds, no views, no functions, no procedures.
My table MYTABLE:
| id | ... | parent_id |
------------------------
| 1 | ... | |
| 2 | ... | 1 |
| 3 | ... | 1 |
| 4 | ... | 2 |
| 5 | ... | 1 |
and I my first statement:
select * from MYTABLE where id in ('1','2','3','4','5');
give my 5 records.
Then I need the information about the first (no deeper) child ids.
My current solution:
for (record in records) {
// get child ids as comma separated string list
// e.g. "2,3,5" for id 1
String childIds = getChildIds(record.id);
}
with the second statement in getChildIds(record.Id):
select id from MYTABLE where parent_id='record.Id';
So I have 1 + 5 = 6 statements for the required information.
I'm looking for a solution to select the records from the following "imaginary" table with the "imaginary" field "child_ids":
| id | ... | parent_id | child_ids |
------------------------------------
| 1 | ... | | 2,3,5 |
| 2 | ... | 1 | 4 |
| 3 | ... | 1 | |
| 4 | ... | 2 | |
| 5 | ... | 1 | |
Does anyone have an idea how I can get this information with only one statement (or with 2 statements)?
Thanks for your help, Thomas
FOR MYSQL:
How about using the GROUP_CONCAT() function like the following:
SELECT id, parent_id,
GROUP_CONCAT(child_id ORDER BY child_id SEPARATOR ',') AS child_ids
FROM MYTABLE
WHERE id IN ('1','2','3','4','5')
FOR ORACLE:
If you have a later version of Oracle you could use the LISTAGG() function:
SELECT parent_id,
LISTAGG(child_id, ', ') WITHIN GROUP (ORDER BY child_id) "child_ids"
FROM MYTABLE
WHERE id IN ('1','2','3','4','5')
GROUP BY parent_id
FOR DERBY:
I don't know anything about derby, but doing a little research it uses IBM DB2 SQL syntax. So, maybe using a combination of XMLSERIALIZE(), XMLAGG(), and XMLTEXT() will work for you:
SELECT parent_id,
XMLSERIALIZE(XMLAGG(XMLTEXT(child_id) ORDER BY child_id) AS CLOB(30K))
FROM table GROUP BY parent_id

MySQL - Select everything from one table, but only first matching value in second table

I'm feeling a little rusty with creating queries in MySQL. I thought I could solve this, but I'm having no luck and searching around doesn't result in anything similar...
Basically, I have two tables. I want to select everything from one table and the matching row from the second table. However, I only want to have the first result from the second table. I hope that makes sense.
The rows in the daily_entries table are unique. There will be one row for each day, but maybe not everyday. The second table notes contains many rows, each of which are associated with ONE row from daily_entries.
Below are examples of my tables;
Table One
mysql> desc daily_entries;
+----------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------+--------------+------+-----+---------+----------------+
| eid | int(11) | NO | PRI | NULL | auto_increment |
| date | date | NO | | NULL | |
| location | varchar(100) | NO | | NULL | |
+----------+--------------+------+-----+---------+----------------+
Table Two
mysql> desc notes;
+---------+---------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------+---------+------+-----+---------+----------------+
| task_id | int(11) | NO | PRI | NULL | auto_increment |
| eid | int(11) | NO | MUL | NULL | |
| notes | text | YES | | NULL | |
+---------+---------+------+-----+---------+----------------+
What I need to do, is select all entries from notes, with only one result from daily_entries.
Below is an example of how I want it to look:
+----------------------------------------------+---------+------------+----------+-----+
| notes | task_id | date | location | eid |
+----------------------------------------------+---------+------------+----------+-----+
| Another note | 3 | 2014-01-02 | Home | 2 |
| Enter a note. | 1 | 2014-01-01 | Away | 1 |
| This is a test note. To see what happens. | 2 | | Away | 1 |
| Testing another note | 4 | | Away | 1 |
+----------------------------------------------+---------+------------+----------+-----+
4 rows in set (0.00 sec)
Below is the query that I currently have:
SELECT notes.notes, notes.task_id, daily_entries.date, daily_entries.location, daily_entries.eid
FROM daily_entries
LEFT JOIN notes ON daily_entries.eid=notes.eid
ORDER BY daily_entries.date DESC
Below is an example of how it looks with my query:
+----------------------------------------------+---------+------------+----------+-----+
| notes | task_id | date | location | eid |
+----------------------------------------------+---------+------------+----------+-----+
| Another note | 3 | 2014-01-02 | Home | 2 |
| Enter a note. | 1 | 2014-01-01 | Away | 1 |
| This is a test note. To see what happens. | 2 | 2014-01-01 | Away | 1 |
| Testing another note | 4 | 2014-01-01 | Away | 1 |
+----------------------------------------------+---------+------------+----------+-----+
4 rows in set (0.00 sec)
At first I thought I could simply GROUP BY daily_entries.date, however that returned only the first row of each matching set. Can this even be done? I would greatly appreciate any help someone can offer. Using Limit at the end of my query obviously limited it to the value that I specified, but applied it to everything which was to be expected.
Basically, there's nothing wrong with your query. I believe it is exactly what you need because it is returning the data you want. You can not look at as if it is duplicating your daily_entries you should be looking at it as if it is return all notes with its associated daily_entry.
Of course, you can achieve what you described in your question (there's an answer already that solve this issue) but think twice before you do it because such nested queries will only add a lot of noticeable performance overhead to your database server.
I'd recommend to keep your query as simple as possible with one single LEFT JOIN (which is all you need) and then let consuming applications manipulate the data and present it the way they need to.
Use mysql's non-standard group by functionality:
SELECT n.notes, n.task_id, de.date, de.location, de.eid
FROM notes n
LEFT JOIN (select * from
(select * from daily_entries ORDER BY date DESC) x
group by eid) de ON de.eid = n.eid
You need to do these queries with explicit filtering for the last row. This example uses a join to do this:
SELECT n.notes, n.task_id, de.date, de.location, de.eid
FROM daily_entries de LEFT JOIN
notes n
ON de.eid = n.eid LEFT JOIN
(select n.eid, min(task_id) as min_task_id
from notes n
group by n.eid
) nmin
on n.task_id = nmin.min_task_id
ORDER BY de.date DESC;