SQLite "split" row into multiple rows - mysql

Is there a way to "split" one row into multiple rows?
My problem is that I have table of edges where patternID is the id of the edge and sourceStationID and targetStationID are the ids that the edge connects:
Patterns:
patternID | sourceStationID | targetStationID
1|1|2
2|1|6
3|1|3
4|1|4
5|4|6
6|5|6
I also have table of Hubs where I can transfer:
Hubs:
hubID
4
5
I need to get out of those data patternIDs that connect stations 1->6 exactly via one hub. So the result of query should be:
4
5
I did that by joining patterns table with hubs table and again with patterns table so i get:
patternID | sourceStationID | targetStationID | patternID | sourceStationID | targetStationID
4 | 1 | 4 | 5 | 4 | 6
How can I split this row into two rows?
edit:
Here is code that I use so far:
select t2a.patternID from
(
select * from `patterns`
join `hubs` on `targetStationID` = `hubID`
where `sourceStationID` = 1
) as t1a
join
(
select * from `patterns`
join `hubs` on `sourceStationID` = `hubID`
where `targetStationID` = 6
) as t2a
on t1a.hubID = t2a.hubID
union
select t1b.patternID from
(
select * from `patterns`
join `hubs` on `targetStationID` = `hubID`
where `sourceStationID` = 1
) as t1b
join
(
select * from `patterns`
join `hubs` on `sourceStationID` = `hubID`
where `targetStationID` = 6
) as t2b
on t1b.hubID = t2b.hubID;
It's working but I'm using the same select twice.

Updating my answer to hopefully be closer to what you wanted. As far as I understand your question, this seems to give what you requested. The view could be avoided, with its join done in the CTE instead:
C:\Users\DDevienne>sqlite3
SQLite version 3.8.4.3 2014-04-03 16:53:12
Enter ".help" for usage hints.
Connected to a transient in-memory database.
Use ".open FILENAME" to reopen on a persistent database.
sqlite> create table edges (id integer primary key autoincrement, beg int, end int);
sqlite> insert into edges (beg, end) values (1, 2), (1, 6), (1, 3), (1, 4), (4, 6), (5, 6);
sqlite> create view edges2 as
...> select start.id as beg_id, start.beg as beg, start.end as hub, finish.id as end_id, finish.end as end
...> from edges start, edges finish
...> where start.end = finish.beg;
sqlite> .headers on
sqlite> select * from edges2;
beg_id|beg|hub|end_id|end
4|1|4|5|6
sqlite> create table hubs (id integer primary key);
sqlite> insert into hubs values (4), (5);
sqlite> with hub_edges(beg_id, beg, hub, end_id, end) as (
...> select edges2.* from edges2, hubs where edges2.hub = hubs.id
...> )
...> select beg_id as id, beg, hub as end from hub_edges
...> union all
...> select end_id as id, hub as beg, end from hub_edges;
id|beg|end
4|1|4
5|4|6
sqlite>
Warning: This requires the latest SQLite (3.8.4.3), and won't work with 3.8.3.x

Related

Is there a way to perform multiple lookups across different columns in two tables in mysql?

I have a table 1 as below (Simplified versions show here as the actual one has over 60+ columns)
ID
Description
From_Index_Code
To_Index_Code
Cost
PARAM1
PARAM2
A
Something.
A001
B001
500
abc.
xyz.
B
Something2.
B001.
C001
1000
abc.
xyz.
I have a master table that is of the following structure:
ID
Code.
Value
1
A001.
100.
2
B001.
200.
3
C001.
300.
Now I have an input that has the following values:
PARAM1=abc,PARAM2=xyz and Index value as 150. I need to check if the index value is between A001 and B001, if yes, return 500. If it is between B001 and C001 then I return 1000.
I tried doing
WITH
src_1 as (select id,s.description,g.value
from table1 s left outer join table 2 g on s.from_index_code=g.code),
src_2 as (select id,s.description,g.value
from table1 s left outer join table 2 g on s.to_index_code=g.code)
select src_1.id, src_1.description,src_1.value as 'from_value',src_2.value as 'to_value' from src_1 ,src_2 where src_1.id=src_2.id.
I expect the resulting set to be something like:
ID
Description
From_Value
To_Value
A
Something.
100.
200.
B
Somethng2.
200.
300.
It should have the same number of rows as Table 1. But the resulting output has far too many rows.
Table 1 has 8497 rows.Table 2 has 121 rows. However the resulting output has over 14 million rows.
What is the best way to accomplish this?
Any help would be much appreciated.
Thanks.
your problem was, that you select id in the subselect and so mysql chooses wrong, adding a s to the ids columns clears everything up and gets the correct result
CREATE TABLE table1
(`id` varchar(1), `Description` varchar(11), `From_Index_Code` varchar(5), `To_Index_Code` varchar(4), `Cost` int, `PARAM1` varchar(4), `PARAM2` varchar(4))
;
INSERT INTO table1
(`id`, `Description`, `From_Index_Code`, `To_Index_Code`, `Cost`, `PARAM1`, `PARAM2`)
VALUES
('A', 'Something.', 'A001', 'B001', 500, 'abc.', 'xyz.'),
('B', 'Something2.', 'B001.', 'C001', 1000, 'abc.', 'xyz.')
;
CREATE TABLE table2
(`id` int, `Code` varchar(4), `Value` int)
;
INSERT INTO table2
(`id`, `Code`, `Value`)
VALUES
(1, 'A001', 100.),
(2, 'B001', 200.),
(3, 'C001', 300.)
;
WITH
src_1 as (select s.id,s.description,g.value
from table1 s left outer join table2 g on s.from_index_code=g.code),
src_2 as (select s.id,s.description,g.value
from table1 s left outer join table2 g on s.to_index_code=g.code)
select src_1.id, src_1.description,src_1.value as 'from_value',src_2.value as 'to_value' from src_1 ,src_2 where src_1.id=src_2.id
id | description | from_value | to_value
:- | :---------- | ---------: | -------:
A | Something. | 100 | 200
B | Something2. | null | 300
db<>fiddle here
Hmmm . . . I am thinking you want joins and conditions like this:
select t1.*
from table1 t1 join
table2 t2f
on t2f.code = t1.From_Index_Code join
table2 t2t
on t2t.code = t1.to_index_code
where t1.param1 = #param1 and t2.param2 = #param2 and
#index_value between t2f.value and t2t.value;
For performance, you would want indexes on table1(param1, param2, from_index_code, to_index_code) and on table2(code) (if it is not already the primary key).

Joining table to union of two tables?

I have two tables: orders and oldorders. Both are structured the same way. I want to union these two tables and then join them to another table: users. Previously I only had orders and users, I am trying to shoehorn oldorders into my current code.
SELECT u.username, COUNT(user) AS cnt
FROM orders o
LEFT JOIN users u
ON u.userident = o.user
WHERE shipped = 1
AND total != 0
GROUP BY user
This finds the number of nonzero total orders all users have made in table orders, but I want to this in the union of orders and oldorders. How can I accomplish this?
create table orders (
user int,
shipped int,
total decimal(4,2)
);
insert into orders values
(5, 1, 28.21),
(5, 1, 24.12),
(5, 1, 19.99),
(5, 1, 59.22);
create table users (
username varchar(100),
userident int
);
insert into users values
("Bob", 5);
Output for this is:
+----------+-----+
| username | cnt |
+----------+-----+
| Bob | 4 |
+----------+-----+
After creating the oldorders table:
create table oldorders (
user int,
shipped int,
total decimal(4,2)
);
insert into oldorders values
(5, 1, 62.94),
(5, 1, 53.21);
The expected output when run on the union of the two tables is:
+----------+-----+
| username | cnt |
+----------+-----+
| Bob | 6 |
+----------+-----+
Just not sure where or how to shoehorn a union into there. Instead of running the query on orders, it needs to be on orders union oldorders. It can be assumed there is no intersect between the two tables.
You just need to union this way:
SELECT u.username, COUNT(user) AS cnt
FROM
(
SELECT * FROM orders
UNION
SELECT * FROM oldorders
) o
LEFT JOIN users u ON u.userident = o.user
WHERE shipped = 1
AND total != 0
GROUP BY user;
First get the combined orders using UNION between orders and oldorders table.
The rest of the work is exactly same what you did.
SEE DEMO
Note:
Left join doesn't make sense in this case. Orders for which the users don't exist then you will get NULL 0 as output. This doesn't hold any value.
If you want <user,total orders> for all users including users who might not have ordered yet then you need to change the order of the LEFT JOIN

SQL limit for LEFT JOINed table

I have the following tables.
Industry(id, name)
Movie(id, name, industry_id) [Industry has many movies]
Trailer(id, name, movie_id) [Movie has many trailers]
I need to find 6 latest trailers for each Industry. Every movie does not need to have a trailer or can have multiple[0-n].
CREATE TABLE industry(id int, name char(10), PRIMARY KEY (id));
CREATE TABLE movie(id int, name char(10), industry_id int, PRIMARY KEY (id),
FOREIGN KEY (industry_id) REFERENCES industry(id));
CREATE TABLE trailer(id int, name char(10), movie_id int, PRIMARY KEY (id),
FOREIGN KEY (movie_id) REFERENCES movie(id));
INSERT INTO industry VALUES (1, "sandalwood");
INSERT INTO industry VALUES (2, "kollywood");
INSERT INTO movie VALUES (1, "lakshmi", 1);
INSERT INTO movie VALUES (2, "saarathi", 2);
INSERT INTO trailer VALUES (1, "lakshmi1", 1);
INSERT INTO trailer VALUES (2, "lakshmi2", 1);
INSERT INTO trailer VALUES (3, "lakshmi3", 1);
INSERT INTO trailer VALUES (4, "lakshmi4", 1);
INSERT INTO trailer VALUES (5, "lakshmi5", 1);
INSERT INTO trailer VALUES (6, "lakshmi6", 1);
INSERT INTO trailer VALUES (7, "saarathi4", 2);
INSERT INTO trailer VALUES (8, "saarathi5", 2);
INSERT INTO trailer VALUES (9, "saarathi6", 2);
SELECT c.*
FROM industry a
LEFT JOIN movie b
ON a.id = b.industry_id
LEFT JOIN trailer c
ON b.id = c.movie_id
LIMIT 0, 6
| ID | NAME | MOVIE_ID |
----------------------------
| 1 | lakshmi1 | 1 |
| 2 | lakshmi2 | 1 |
| 3 | lakshmi3 | 1 |
| 4 | lakshmi4 | 1 |
| 5 | lakshmi5 | 1 |
| 6 | lakshmi6 | 1 |
I need to fetch only one recent trailer from each movie. But I am getting all trailers for each movie. Please suggest me to get the SQL statement.
I'm not sure if this works in MySql or not because I can't remember if you can have subqueries inside of an in clause, but you might try:
select * from trailer
where id in (select max(id) from trailer group by movie_id)
Whether it works or not, it looks like you're not using the industry table in your query so there's not much point in joining to it (unless you are actually trying to exclude movies that don't have any industry assigned to them... but based on your sample I it doesn't look like that was your intention).
If the above query doesn't work in MySql, then try this one
select t.*
from trailer t join
(select max(id) id from trailer group by movie_id) t2 on t1.id = t2.id
To get recent trailor you should include date field column from which we can fetch it
If you must do this all in SQL (and not in whatever backend or code you are using, which I would actually recommend) then you are probably going to have to rely on some variable magic.
Essentially, you need to "rank" each trailer by the date and then "partition" it by the movie that the trailer belongs to. These words have actual meaning in some other flavors of SQL (such as PL/SQL) but unfortunately don't have native functionality in MySQL.
You're going to want do to something similar to what is mentioned in this SO post. Once you get the "ranks" in there partitioned by movie_id, you just select WHERE rank < 6. The query could get pretty messy and there is some risk in using variables in that way but from what I can tell this is the best way to do it strictly with a MySQL query
Try this query
SELECT * FROM industry
LEFT JOIN movie on movie.industry_id = industry.id
LEFT JOIN (
SELECT
id as T_ID,
name as T_Name,
movie_id
FROM trailer
INNER JOIN ( SELECT
MAX(id) as TID
FROM trailer
GROUP BY movie_id
) as t on t.TID = trailer.id
) as c on c.movie_id = movie.id;
Here is the Demo
SELECT i.name, m.name, MAX(t.id) AS 'Latest Trailer ID', MAX(t.name) AS 'Latest Trailer'
FROM industry i
INNER JOIN movie m ON(i.id = m.industry_id)
INNER JOIN trailer t ON(m.id = movie_id)
GROUP BY m.id
If you want latest trailer by id of trailer table then use below query:
SELECT * FROM trailer t
INNER JOIN (SELECT movie_id, MAX(id) id
FROM trailer GROUP BY movie_id) AS A ON t.id = A.id
OR If you want data latest by date then use this query:
SELECT * FROM trailer t
INNER JOIN (SELECT movie_id, MAX(latestbydate) latestbydate
FROM trailer GROUP BY movie_id
) AS A ON t.movie_id = A.movie_id AND t.latestbydate = A.latestbydate

Create an inline SQL table on the fly (for an excluding left join)

Let's assume the following:
Table A
id | value
----------
1 | red
2 | orange
5 | yellow
10 | green
11 | blue
12 | indigo
20 | violet
I have a list of id's (10, 11, 12, 13, 14) that can be used to look up id's in this table. This list of id's is generated in my frontend.
Using purely SQL, I need to select the id's from this list (10, 11, 12, 13, 14) that do not have entries in Table A (joining on the 'id' column). The result should be the resultset of id's 13 and 14.
How can I accomplish this using only SQL? (Also, I'd like to avoid using a stored procedure if possible)
The only approach I can think of is something that would create an inline SQL table on the fly to temporarily hold my list of id's. However, I have no idea how to do this. Is this possible? Is there a better way?
Thanks! :)
You can do this from SQL Server 2008 onwards using a table value constructor.
SELECT * FROM (
VALUES(1, 'red'),
(2, 'orange'),
(5, 'yellow'),
(10, 'green'),
(11, 'blue'),
(12, 'indigo'),
(20, 'violet'))
AS Colors(Id, Value)
More information here:
Table Value Constructor
You can create an "inline table" with a UNION subquery:
(
SELECT 10 AS id
UNION ALL SELECT 11 UNION ALL SELECT 12 UNION ALL SELECT 13 UNION ALL SELECT 14
-- etc.
) AS inline_table
CREATE TEMPORARY TABLE ids (id INT NOT NULL PRIMARY KEY);
INSERT
INTO ids
VALUES
(10),
(11),
(12),
(13),
(14);
SELECT *
FROM ids
WHERE id NOT IN
(
SELECT id
FROM a
);
Something like this will work too
SELECT * FROM (
SELECT 'ds' AS source
UNION ALL
SELECT 'cache' AS source
) as dataSource
----------
| source |
----------
| ds |
----------
| cache |
----------
create table B (id int)
insert into B values (10),(11),(12),(13),(14)
select *
from B
left join A
on A.id=B.id
where A.id is null
drop table B
http://sqlfiddle.com/#!6/6666c1/30

MySQL self join question

Take a look at the following mySQL query:
SELECT fname,lname FROM users WHERE users.id IN (SELECT sub FROM friends WHERE friends.dom = 1 )
The above query first creates a set of ALL the friends.sub's via the inner query, and then the outer query selects a list of users where user ids are contained within the set created by the inner query (ie the union of the two sets).
And this works fine. But if you needed the inner set to contain not only the subs where dom = 1, but also the doms where sub = 1, like so:
Outer query remains same as above, pure pseudocode:
(SELECT sub FROM friends WHERE friends.dom = 1 )
***AND***
(SELECT dom FROM friends WHERE friends.sub = 1 )
Is it possible to make this sort of functionality with the inner query??
Any help or assistance appreciated guys;-D
Thanks a lot guys, my headache is gone now!
Try this:
SELECT u.fname, u.lname
FROM users u
INNER JOIN friends f
ON (u.id = f.sub AND f.dom = 1)
OR (u.id = f.dom AND f.sub = 1)
I'm not sure if I correctly understand what sub and dom represent, but it looks like you can use a UNION in there:
SELECT fname, lname
FROM users
WHERE users.id IN
(
SELECT sub FROM friends WHERE friends.dom = 1
UNION
SELECT dom FROM friends WHERE friends.sub = 1
);
Test case:
CREATE TABLE users (id int, fname varchar(10), lname varchar(10));
CREATE TABLE friends (dom int, sub int);
INSERT INTO users VALUES (1, 'Bob', 'Smith');
INSERT INTO users VALUES (2, 'Peter', 'Brown');
INSERT INTO users VALUES (3, 'Jack', 'Green');
INSERT INTO users VALUES (4, 'Kevin', 'Jackson');
INSERT INTO users VALUES (5, 'Steven', 'Black');
INSERT INTO friends VALUES (1, 2);
INSERT INTO friends VALUES (1, 3);
INSERT INTO friends VALUES (4, 1);
INSERT INTO friends VALUES (3, 4);
INSERT INTO friends VALUES (5, 2);
Result:
+-------+---------+
| fname | lname |
+-------+---------+
| Peter | Brown |
| Jack | Green |
| Kevin | Jackson |
+-------+---------+
3 rows in set (0.00 sec)
That said, #Alec's solution is probably more efficient.