SQL select statement optimizing (id, parent_id, child_ids) - mysql

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

Related

JSON extract multiple columns PostgreSQL

I had a question earlier: PostgreSQL trim text field with regex (or else) And I got a wonderful answer by a_horse_with_no_name. Now I have an additional question regarding this issue.
So here it is this rextester https://rextester.com/SUWG96428 and the goal is to have all the ids in a separate column. Is it possible at all?
Like this:
+---+----+-------+-------+
| | id | ids_1 | ids_2 |
+---+----+-------+-------+
| 1 | 1 | 4202 | 4203 |
| 2 | 2 | 4204 | |
| 3 | 3 | 4201 | |
+---+----+-------+-------+
Yep, you can modify your query like:
select
t.id,
right(((the_column::json->'itemID')->>0)::varchar, 4) as col1,
right(((the_column::json->'itemID')->>1)::varchar, 4) as col2,
right(((the_column::json->'itemID')->>2)::varchar, 4) as col3
from the_table t;
DB Fiddle

MySQL query to return a value if valus in two different rows match a condition

In the example below, I'm trying to create a query that returns the content of NAME if this contains both 'ammonium nitrate' and 'urea'. The thing is, they are in separate rows and I haven't found how to check for both. I've tried using the clauses IN, AND, and UNION, but to no avail.
The query should return only 'Gro-Fast', as it is the only NAME that contains both.
+-------+------------------+-------------+---------+
| FCODE | CNAME | NAME | CONTACT |
+-------+------------------+-------------+---------+
| 28994 | ammonium nitrate | Gro-Fast | 556698 |
| 28994 | urea | Gro-Fast | 556698 |
| 29462 | ammonium nitrate | BetterRoots | 342554 |
| 34588 | ammonium nitrate | Flourisher | 342554 |
| 83732 | urea | GreenAgain | 354211 |
+-------+------------------+-------------+---------+
TIA!
You can group by name and set the conditions in the HAVING clause:
select name
from tablename
where cname in ('ammonium nitrate', 'urea')
group by name
having count(distinct cname) = 2
If you want the column cnameto contain only 'ammonium nitrate' and 'urea':
select name
from tablename
group by name
having
sum(cname not in ('ammonium nitrate', 'urea')) = 0
and
count(distinct cname) = 2
See the demo.
Results:
> | name |
> | :------- |
> | Gro-Fast |
Edit, for your query:
select name
from CONTENTS natural join FERTILIZERS
where cname in ('ammonium nitrate', 'urea')
group by name
having count(distinct cname) = 2

Mysql - Compare int field with comma separated field from another table

I have two tables in a MySQL database like this:
User:
userid |userid | Username | Plan(VARCHAR) | Status |
-----------+------------+--------------+---------------+---------+
1 | 1 | John | 1,2,3 |1 |
2 | 2 | Cynthia | 1,2 |1 |
3 | 3 | Charles | 2,3,4 |1 |
Plan: (planid is primary key)
planid(INT) | Plan_Name | Cost | status |
-------------+----------------+----------+--------------+
1 | Tamil Pack | 100 | ACTIVE |
2 | English Pack | 100 | ACTIVE |
3 | SportsPack | 100 | ACTIVE |
4 | KidsPack | 100 | ACTIVE |
OUTPUT
id |userid | Username | Plan | Planname |
---+-------+----------+------------+-------------------------------------+
1 | 1 | John | 1,2,3 |Tamil Pack,English Pack,SportsPack |
2 | 2 | Cynthia | 1,2 |Tamil Pack,English Pack |
3 | 3 | Charles | 2,3,4 |English Pack,Sportspack, Kidspack |
Since plan id in Plan table is integer and the user can hold many plans, its stored as comma separated as varchar, so when i try with IN condition its not working.
SELECT * FROM plan WHERE find_in_set(plan_id,(select user.planid from user where user.userid=1))
This get me the 3 rows from plan table but i want the desired output as above.
How to do that.? any help Please
A rewrite off your query what should work is as follows..
Query
SELECT
all columns you need
, GROUP_CONCAT(Plan.Plan_Name ORDER BY Plan.planid) AS Planname
FROM
Plan
WHERE
FIND_IN_SET(Plan.plan_id,(
SELECT
User.Plan
FROM
user
WHERE User.userid = 1
)
)
GROUP BY
all columns what are in the select (NOT the GROUP_CONCAT function)
You also can use FIND_IN_SET on the ON clause off a INNER JOIN.
One problem is that the join won't ever use indexes.
Query
SELECT
all columns you need
, GROUP_CONCAT(Plan.Plan_Name ORDER BY Plan.planid) AS Planname
FROM
User
INNER JOIN
Plan
ON
FIND_IN_SET(Plan.id, User.Plan)
WHERE
User.id = 1
GROUP BY
all columns what are in the select (NOT the GROUP_CONCAT function)
Like i said in the comments you should normalize the table structures and add the table User_Plan whats holds the relations between the table User and Plan.

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)

Transforming one record into multiple records of a resultset

I'm creating a MySQL view for a server application that connects directly to the database server. I didn't like the way the application's schema is so I went with my own structure. Originally, I was going to use the data to create static configuration files, but that is no longer an option.
Anyway, I'll be using several rows from different tables to create this view. There is a 1:many relationship between table A and the view and a 1:1 relationship for table B. It's the 1:many that's throwing me off.
In table A, I have the following:
+--------------------------------+
| id | name | timeout | any |
+--------------------------------+
| 1 | Main | 10 | 1 |
+--------------------------------+
In table B, I have the following:
+-------------------+
| id | a_id | route |
+-------------------+
| 1 | 1 | 123 |
+-------------------+
| 2 | 1 | 321 |
+-------------------+
For the view, the two tables will be joined like so:
+-------------------------------------+
| name | app | data |
+-------------------------------------+
| Main | timeout | 10 |
+-------------------------------------+
| Main | dialany | 1 |
+-------------------------------------+
| Main | routeto | 321 |
+-------------------------------------+
| Main | routeto | 123 |
+-------------------------------------+
I'm not even sure if there's a name for this (or if it's even possible) but any help to get started would be fantastic.
You'd use a UNION to get the individual rows from the first table, something like this:
SELECT name, 'timeout' as app, timeout FROM A WHERE id = 1
UNION SELECT name, 'dialany' as app, `any` FROM A WHERE id = 1
And then UNION in the other table:
UNION SELECT A.name, 'routeto' as app, B.data FROM A
JOIN B ON A.id = B.a_id
WHERE A.id = 1
And you should have what you want.