i've a relationa table build from a csv file that contains, external keys on each row.
table structure:
| id | tree/nome | tree/regioni/0 | .... | tree/regioni/n
"n" is equal to 36!!!
I have build a table "regions" from a "SELECT/DINSTICT" and I added an id on it;
now I need to build another table to extrat the "external keys" "tree/regioni/n" with the "id" of each row.
The NOT smart way that ive found to achieve this is to build the table from a SELECT/UNION of each 'tree/regioni/n':
SELECT `id`,`tree/regioni/0`
FROM `trees` WHERE 1
UNION
SELECT `id`,`tree/regioni/1`
FROM `trees` WHERE 1
[.....]
UNION
SELECT `id`,`tree/regioni/N`
FROM `trees`
WHERE 1
Your method is fine. If trees where a view, then you would have the problem of evaluating the view multiple times. Even with a table, you have to scan the table multiple times.
Assuming you do not have duplicates that you want removed, change union to union all. You can also use a cross join:
select t.id,
(case when n = 1 then `tree/regioni/0`
when n = 2 then `tree/regioni/1`
. . .
end)
from t cross join
(select 0 as n union all
select 1 as n union all
. . .
) x;
This looks more complicated but it only scans/evaluates the table expressions once.
Related
When I use in keyword in sql, there may be some id is missing , but I want treat them like they exist and other columns are null or 0.
For example, suppose I have a table with two columns and some rows:
[id,value1]
1 1
2 4
3 3
5 5
I may write sql like this:
select * from table where id in (1,4,5) order by value1 limit 0,2 ;
When this sql is executed, the return result is [(1,1),(5,5)].
But what I want is [(4,0),(1,1)], because I want to treat the missing id 4 like it exists in the table.
So the question is : Is there some elegant way to achieve it using sql instead of select all rows and sort them in memory.
Use a left join:
select *
from (select 1 as id union all
select 4 union all
select 5
) i left join
table t
using (id)
order by t.value1
limit 0, 2 ;
Note that you are ordering by a value in the existing table, so this depends on the fact that NULL is ordered before other values.
I have a contents table and the entires in it are as shown in the attached figure
There are more than 100,000 entries. I want to fetch the data where the update_date for commit=0 is greater than update_date for commit=1. I also need the corresponding row for commit=1.
I tried a few things, but takes a long time to retrieve the results. What is the best SQL query I can use. I am using MySQL database.
EDIT
I have now updated the table. There is an attribute called content_id which binds the rows together.
A query like this gives me half of what I want
select a.* from contents a, contents b where
a.content_id=b.content_id and
a.update_date > b.update_date and
a.committed=0 and b.committed=1
I also want the corresponding entries from committed=1, but they should be appended at the bottom as rows and not vertically concatenated as columns.
For example, I cannot use
select * from contents a, contents b where
a.content_id=b.content_id and
a.update_date > b.update_date and
a.committed=0 and b.committed=1
because the results from 'b' are appended vertically. Also, is there a better way to write this query. This works really slow if there are many entries in the database.
I am assuming that in the above example, you only need id=2 as for content id = 1, the update_date for commit=0 is greater than update_date for commit=1 and in that case you need data for commited = 1.
I an using Oracle, so you need to find a suitable replacement for row_number() funtion in mysql.
The logic would be
Create a view on the existing table to use rownumber so it will give rownumber like below order by time desc (see if you use a nested query to do it)
ID, CONTENT_ID, COMMITED, UPDATE_DATE, ROWN
2 1 1 06-SEP-15 00:00:56 1
1 1 0 07-SEP-15 00:00:56 2
3 2 0 03-SEP-15 00:00:56 1
4 2 1 04-SEP-15 00:00:56 2
Now select only rows where where rown=1 and commited=1
This is the query in oracle. The second with query c2 will be your view.
Oracle query
with c1 (id, content_id,commited,update_date) as
(
select 1,1,0,sysdate from dual union
select 2,1,1,sysdate-1 from dual union
select 3,2,0,sysdate-4 from dual union
select 4,2,1,sysdate-3 from dual
),
c2 as
(select c1.*,row_number() over(partition by content_id order by update_date) as rown from c1)
select id,content_id,commited,update_date from c2
where rown=1 and commited=1
ID, CONTENT_ID, COMMITED, UPDATE_DATE, ROWN
Output
ID, CONTENT_ID, COMMITED, UPDATE_DATE
2 1 1 06-SEP-15 00:06:17
Is there a function like "unnest" from POSTGRESQL on MYSQL?
Query (PSQL):
select unnest('{1,2,3,4}'::int[])
Result (as table):
int |
_____|
1 |
_____|
2 |
_____|
3 |
_____|
4 |
_____|
Short answer
Yes, it is possible. From technical viewpoint, you can achieve that with one query. But the thing is - most probably, you are trying to pass some logic from application to data storage. Data storage is intended to store data, not to represent/format it or, even more, apply some logic to it.
Yes, MySQL doesn't have arrays data type, but in most cases it won't be a problem and architecture can be created so it will fit those limitations. And in any case, even if you'll achieve it somehow (like - see below) - you won't be possible to properly work later with that data, since it will be just result set. You may store it, of course - so to, let's say, index later, but then it's again a task for an application - so to create that import.
Also, make sure that it is not a Jaywalker case, so not about storing delimiter-separated values and later trying to extract them.
Long answer
From technical viewpoint, you can do it with Cartesian product of the two row sets. Then use a well known formula:
N = d1x101 + d2x102 + ...
Thus, you'll be able to create a "all-numbers" table and later iterate through it. That iteration, together with MySQL string functions, may lead you to something like this:
SELECT
data
FROM (
SELECT
#next:=LOCATE(#separator,#search, #current+1) AS next,
SUBSTR(SUBSTR(#search, #current, #next-#current), #length+1) AS data,
#next:=IF(#next, #next, NULL) AS marker,
#current:=#next AS current
FROM
(SELECT 0 as i UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) as n1
CROSS JOIN
(SELECT 0 as i UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) as n2
CROSS JOIN
(SELECT
-- set your separator here:
#separator := ',',
-- set your string here:
#data := '1,25,42,71',
-- and do not touch here:
#current := 1,
#search := CONCAT(#separator, #data, #separator),
#length := CHAR_LENGTH(#separator)) AS init
) AS joins
WHERE
marker IS NOT NULL
The corresponding fiddle would be here.
You should also notice: this is not a function. And with functions (I mean, user-defined with CREATE FUNCTION statement) it's impossible to get result row set since function in MySQL can not return result set by definition. However, it's not true to say that it's completely impossible to perform requested transformation with MySQL.
But remember: if you are able to do something, that doesn't mean you should do it.
This sample fetchs all "catchwords" from Table data, wich are seperated by ","
Maximum values in the commaseparated list is 100
WITH RECURSIVE num (n) AS (
SELECT 1
UNION ALL
SELECT n+1 FROM num WHERE n<100 -- change this, if more than 100 elements
)
SELECT DISTINCT substring_index(substring_index(catchwords, ',', n), ',', -1) as value
FROM data
JOIN num
ON char_length(catchwords) - char_length(replace(catchwords, ',', '')) >= n - 1
In newer Version of MySQL/MariaDB you can use JSON_TABLE if you can JOIN the elements:
SELECT cat.catchword, dat.*
FROM data dat
CROSS JOIN json_table(concat('[',dat.catchwords, ']')
, '$[*]' COLUMNS(
catchword VARCHAR(50) PATH '$'
)
) AS words
I have following data in MySQL table named info:
chapter | section
3 | 0
3 | 1
3 | 2
3 | 3
4 | 0
5 | 0
I would like to delete a row for chapter = n, but only when there is no section>0 for same chapter. So chapter 3 can't be deleted while chapter 4 and 5 can. I know the following doesn't work:
DELETE info WHERE chapter = 3 AND NOT EXISTS (SELECT * FROM info WHERE chapter = 3 AND section>0);
The same table is used twice in the statement. So what is the easiest way to achieve my goal?
You've got the idea right. Here is the syntax:
DELETE
FROM mytable
WHERE chapter NOT IN (
SELECT * FROM (
select tt.chapter
from mytable tt
where tt.section <> 0
group by tt.chapter
) tmp
)
The nested select is a workaround a bug in MySQL.
Demo.
You can run a sub query to return the rows that have sections of more then one and then delete the rows returned from the sub query.
DELETE FROM table1 WHERE table1.chapter Not IN (select chapter from
(SELECT table1.chapter FROM table1 WHERE Table1.section >=1 ) Results);
Example Fiddle based on your question
You could also supply the chapter as well in the sub query where clause if you only want to delete a specfic chapter. If it does not meet the where clause then no records will be deleted.
This should do it.
DELETE FROM Table t
WHERE NOT EXISTS(
SELECT 1
FROM Table t2
Where t2.chapter = t.chapter
And t2.section > 0
)
In my experience Exists generally performs better than In. If you are storing a large amount of records you should take this into consideration.
Got this:
Table a
ID RelatedBs
1 NULL
2 NULL
Table b
AID ID
1 1
1 2
1 3
2 4
2 5
2 6
Need Table a to have a comma separated list as given in table b. And then table b will become obsolete:
Table a
ID RelatedBs
1 1,2,3
2 4,5,6
This does not rund through all records, but just ad one 'b' to 'table a'
UPDATE a, b
SET relatedbs = CONCAT(relatedbs,',',b.id)
WHERE a.id = b.aid
UPDATE: Thanks, 3 correct answers (marked oldest as answer)! GROUP_CONCAT is the one to use. No need to insert commas between the ids using relatedids = CONCAT(relatedids,',',next_id) that is done automatic by GROUP_CONCAT.
You'll have to use the mysql group_concat function in order to achieve this: http://dev.mysql.com/doc/refman/5.1/en/group-by-functions.html#function_group-concat
Look into GROUP_CONCAT(expr)
mysql> SELECT student_name,
-> GROUP_CONCAT(DISTINCT test_score
-> ORDER BY test_score DESC SEPARATOR " ")
-> FROM student
-> GROUP BY student_name;
You can't do that in standard SQL. You could write a stored procedure to do that. I had a similar problem, but I was using PostgreSQL so I was able to resolve it by writing a custom aggregate function so that you can do queries like
select aid, concat(id)
from b group by
aid
Update: MySQL has a group_concat aggregate function so you can do something like
SELECT id,GROUP_CONCAT(client_id) FROM services WHERE id = 3 GROUP BY id
as outlined here.