I am trying to reuse a table in SQLite. My attempt is as follows:
SELECT
Partials.e_sentence
FROM
(SELECT
e_sentence, _id
FROM
Pair
JOIN PairCategories
ON
_id=PairId AND CategoryId=53
UNION
SELECT
e_sentence, _id
FROM
Pair
WHERE
e_sentence LIKE '%' || 'how often' || '%'
GROUP BY
e_sentence)
AS Parents JOIN Partials
ON Parents._id=ParentId
UNION
SELECT
e_sentence
FROM
Parents
The key part I am trying to accomplish is at the bottom, where I try to UNION a table created in the previous statement. Is there a way to do this in SQLite, or am I forced to repeat the query that made the Parents table in the first half of the UNION?
In SQLite 3.8.3 or later, you can use a common table expression:
WITH Parents AS (
SELECT e_sentence, _id
FROM Pair
JOIN PairCategories
...
)
SELECT Partials.e_sentence
FROM Parents
JOIN Partials ON Parents._id = ParentId
UNION
SELECT e_sentence
FROM Parents;
If you're using an older SQLite (probably because you're using an older Android), you can create a view for the subquery:
CREATE VIEW Parents AS
SELECT e_sentence, _id
FROM Pair
JOIN PairCategories
...;
SELECT Partials.e_sentence
FROM Parents
JOIN Partials ON Parents._id = ParentId
UNION
SELECT e_sentence
FROM Parents;
If you do not want to have this view permanently in the database, you could make it temporary (CREATE TEMPORARY VIEW ...) so that it is not available outside the current database connection, or, as last resort, you could just insert the subquery wherever you would use Parent:
SELECT Partials.e_sentence
FROM (SELECT ...) AS Parents
JOIN Partials ON Parents._id = ParentId
UNION
SELECT e_sentence
FROM (SELECT ...) AS Parents;
Related
I haev a query where I have a CTE that selects some rows in a specfic order, and I want to use that same order for my main data set Im returning:
WITH selector (id) AS (SELECT id FROM ..... ORDER BY something)
SELECT ...
FROM users u
JOIN selector s ON s.id = u.id
ORDER BY FIELD(u.id, (SELECT id FROM selector))
but this isn't valid syntax in the last ORDER BY FIELD statement as the sub query returns more than one row, is it possible to achieve something like this?
The function FIELD() needs a list of values and not the results of a query.
You can use the function GROUP_CONCAT() to create a comma separated list of ids returned by the CTE, ordered by your conditions and then the function FIND_IN_SET() to join the CTE to the table and sort by its result:
WITH selector (ids) AS (SELECT GROUP_CONCAT(id ORDER BY something) FROM .....)
SELECT ...
FROM users u INNER JOIN selector s
ON FIND_IN_SET(u.id, s.ids)
ORDER BY FIND_IN_SET(u.id, s.ids)
Or, use ROW_NUMBER() window function:
WITH selector (id, rn) AS (SELECT id, ROW_NUMBER() OVER (ORDER BY something) FROM .....)
SELECT ...
FROM users u INNER JOIN selector s
ON s.id = u.id
ORDER BY s.rn
I have two tables: Player(name, email) and TeamPlayer(PlayerName, Team, Active). Tables Player and TeamPlayer are connected via Player.name = TeamPlayer.PlayerName.
I want to get all the elements of TeamPlayer, but also the elements in Player not in TeamPlayer. The attribute Active is important also. I execute the next command, but it did not work:
SELECT DISTINCT Player.Name, TeamPlayer.Team, TeamPlayer.Active FROM Player LEFT JOIN TeamPlayer ON TeamPlayer.PlayerName=Player.Name ORDER BY TeamPlayer.Active;
Is it possible?
You can use UNION ALL for the table TeamPlayer and the rows from Player that don't exist in TeamPlayer:
SELECT PlayerName AS Name, Team, null AS email, Active
FROM TeamPlayer
UNION ALL
SELECT t.Name, null, t.email, 0
FROM Player t
WHERE NOT EXISTS (
SELECT 1 FROM TeamPlayer
WHERE PlayerName = t.Name
)
ORDER BY Active
Since the 2 tables don't have the same columns, the non existing columns for each case will be NULL or 0 for the column Active.
You can change it as you wish.
I have a query that I run in PostgreSQL like this:
select
c_count, count(*) as custdist
from (
select
c_custkey,
count(o_orderkey)
from
customer left outer join orders on
c_custkey = o_custkey
and o_comment not like '%special%requests%'
group by
c_custkey
)as c_orders (c_custkey, c_count)
group by
c_count
order by
custdist desc,
c_count desc;
And I wanted to run it on SQLite, but I got this error: Error: near" (": syntax error. Maybe he doesn't recognize this as c_orders (c_custkey, c_count).
Is there any way to rewrite this query to execute in SQLite?
SQLite does not allow redefining/renaming columns for an nested, aliased query. You can do that with a WITH clause (i.e. Common Table Expression; CTE). Or you can add aliases to the nested query columns directly using AS keyword.
Interesting that this is exactly how the outer query columns are named. Just use the same pattern for the nested query. I don't use PostgreSQL, but why not add aliases directly on each column and complicate it by using different syntax for each part of the query?
select
c_count, count(*) as custdist
from (
select
c_custkey,
count(o_orderkey) AS c_count
from
customer left outer join orders on
c_custkey = o_custkey
and o_comment not like '%special%requests%'
group by
c_custkey
) AS c_orders
group by
c_count
order by
custdist desc,
c_count desc;
Assume the two tables reside in the same file. So the question is, other than their names, are the tables identical, i.e., same schema, same contents.
To compare the table schemas, look at the statements in the sqlite_master table:
SELECT sql FROM sqlite_master WHERE tbl_name IN ('This', 'That');
You have to ignore the table name itself in the comparison; automatic replacement is harder if you have any column names or comments that containt the table name.
To compare the contents, just use compound queries to check whether there are any rows that are not in the other table:
SELECT NOT EXISTS (SELECT * FROM This
EXCEPT
SELECT * FROM That)
AND NOT EXISTS (SELECT * FROM That
EXCEPT
SELECT * FROM This);
I believe that the following may suffice :-
/*
Compare Schema and data in tables.
t1 is the SQL for the first table (mytable) with the table name changed to a common name (table)
t2 is the SQL for the second table (mytable_copy1) with the table changed to a common name (table)
(so if t1 and t2 are equal then the schema is the same)
tablecompare is the logical result of comparing the data of each table with the other table
except matching rows so if no rows exists then NOT EXISTS will be true (1) AND the two
and the result will be 1 if both tables exactly match each other.
*/
SELECT
(t1 = t1) AND tablecompare AS test FROM
(SELECT
replace(sql,'mytable','table') AS t1, -- change the table name to a common name
replace(sql,'mytable_copy1','table') AS t2, -- change the table name to a common name
(
SELECT NOT EXISTS (SELECT * FROM mytable EXCEPT SELECT * FROM mytable_copy1)
AND NOT EXISTS (SELECT * FROM mytable_copy1 EXCEPT SELECT * FROM mytable)
) AS tablecompare
FROM sqlite_master WHERE name = 'mytable'
)
Note the tables are mytable and mytable_copy1 so these would be changed to reflect the two tables.
Perhaps less confusing (perhaps more) is this more long-winded solution :-
/*
Table Compare
t1 is the SQL for the first table (mytable) with the table name replace by table
t2 is the SQL for the second table (mytable_copy1) again table name change to table
So if t1 = t2 then the schema is identical
t1count is the number of rows in the first table
t2count is the number of rows in the second table
So if the counts are the same then the tables may be identical
unioncount is the count of the union of the two tables (not union all) so duiplicates are dropped
therefore if unioncount is the same as either of the table counts then tables are identical
NOTE!!! this assumes tables are not WITHOUT ROWID tables (would have to omit the inclusion of rowid NOT TESTED)
the inclusion of rowid could be dropped (NOT TESTED) if there is an alias of rowid.
*/
SELECT
t1 = t1 AND t1count = t2count AND t1count = unioncount AS test FROM
(SELECT
replace(sql,'mytable','table') AS t1, -- change the table name to a common name
replace(sql,'mytable_copy1','table') AS t2, -- change the table name to a common name
(SELECT count() FROM mytable) AS t1count, -- get the number of rows
(SELECT count() FROM mytable_copy1) AS t2count, -- get the number of rows
(SELECT count() AS unioncount FROM
(SELECT rowid,* FROM mytable UNION SELECT rowid,* FROM mytable_copy1)) AS unioncount
FROM sqlite_master WHERE name = 'mytable'
) ;
Both solutions return a single row/column result 1 if the tables match 0 if they do not.
It could however, but less time consuming/resource hungry to do individual tests. e.g. if the schema's don't match do nothing otherwise check the row counts and if they don't match don't do the final check of actually checking the data.
Test the following tables were used for testing :-
For mytable and mytable_copy1 :-
The above both produced 1 as per :-
and :-
When the following table (mytable_copy2 with changed data highlighted) :-
The results were :-
and :-
As a training exercise I'm working on a fictional SQLite database resembling League of Legends, and I need to perform a left outer join to get a table of all players and if they have skins that are not called 'Classic', return those too.
I currently have this query:
SELECT * FROM players
LEFT OUTER JOIN (SELECT * FROM playerchampions WHERE NOT championskin = 'Classic')
ON name = playername
Which returns what I am looking for, but also a lot of columns I don't want (player experience, player IP, player RP, playername in the playerchampions table. The code for the two tables is as following:
CREATE TABLE players (
name TEXT PRIMARY KEY,
experience INTEGER,
currencyip INTEGER,
currencyrp INTEGER
);
CREATE TABLE playerchampions (
playername TEXT REFERENCES players ( name ) ON UPDATE CASCADE,
championname TEXT REFERENCES champions ( name ) ON UPDATE CASCADE,
championskin TEXT REFERENCES skins ( skinname ) ON UPDATE CASCADE,
PRIMARY KEY ( playername, championname, championskin )
);
As I said, the query executes, but I can't use SELECT players.name, playerchampions.championname, playerchampions.championskin as the playerchampions columns are not given their proper table name when returned.
How do I fix this?
Try using aliases:
SELECT p.name, c.championskin FROM players p LEFT OUTER JOIN (SELECT pc.playername playername, pc.championskin championskin FROM playerchampions pc WHERE NOT pc.championskin = 'Classic') c ON p.name = c.playername;
Not sure if its exactly what you need, but it will get you closer...
SELECT * FROM players p LEFT OUTER JOIN playerchampions pc ON (p.name = pc.playername) WHERE NOT pc.championskin = 'Classic'