How can I get values values from a table and also not in that table in SQLite? - sqlite

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.

Related

Insert multiple values with the same foreign key

I have two tables that reference each other:
CREATE TABLE Room
room_id INTEGER PRIMARY KEY,
room_name TEXT UNIQUE NOT NULL;
CREATE TABLE Item
item_id INTEGER PRIMARY KEY,
room_id INTEGER,
item_name TEXT,
FOREIGN KEY (room_id) REFERENCES Room (room_id) ON UPDATE RESTRICT ON DELETE RESTRICT;
Now I want to add a new room and add a few dozen items to go into it.
INSERT INTO Room(room_name) VALUES ('Living Room');
Let's say I don't know how many rooms there are, and I just want to put stuff into the living room. To do that, I need to select the right room_id. For a single item this is not too bad:
INSERT INTO Item(room_id, item_name)
SELECT room_id, 'Couch' AS item_name FROM Room WHERE room_name = 'Living Room';
But what if I want to insert a bunch of values simultaneously. I tried using last_insert_rowid, but that does not treat the entire INSERT as a single transaction. In other words, the last ID keeps incrementing
INSERT INTO Item (room_id, item_name)
VALUES
(last_insert_rowid(), 'Chair'),
(last_insert_rowid(), 'TV'),
(last_insert_rowid(), 'Carpet');
I would like to avoid having to use the SELECT on each new row. Is there a way to insert multiple values into Item, while referencing the last known room_id in Room?
Something in the nature of a CROSS JOIN would likely be very useful, but I don't know how to get the constants to behave in that case
The end result I am looking for is for Room to look like this:
room_id | room_name
--------+-----------
1 | Living Room
And Item like this:
item_id | room_id | item_name
--------+---------+-----------
1 | 1 | Chair
2 | 1 | TV
3 | 1 | Carpet
You can use a CROSS join of the id that you get from the new room to a CTE that returns the items that you want to insert:
WITH cte(item_name) AS (VALUES ('Chair'), ('TV'), ('Carpet'))
INSERT INTO Item (room_id, item_name)
SELECT r.room_id, c.item_name
FROM Room r CROSS JOIN cte c
WHERE r.room_name = 'Living Room';
See the demo.
If you are using a version of SQLite that does not support CTEs use UNION ALL in a subquery:
INSERT INTO Item (room_id, item_name)
SELECT r.room_id, c.item_name
FROM Room r
CROSS JOIN (
SELECT 'Chair' item_name UNION ALL
SELECT 'TV' UNION ALL
SELECT 'Carpet'
) c
WHERE r.room_name = 'Living Room';
See the demo.

how to use join with Sqlite Index

I am creating a app with ionic 3. In the app I added a search filter which filter users according to their role. I have 5 tables from which I have to pull the data.In term to do so, I did these steps..
created each table index with the required data.
CREATE INDEX IF NOT EXISTS AccountIndexTable ON Accounts(id,roleId,firstName,lastName,email,accountId);
CREATE INDEX IF NOT EXISTS AddressIndexTable ON Addresses(id,addressTypeId,street,city,state,country,pincode,accountId)
CREATE INDEX IF NOT EXISTS CompanyIndexTable ON Companies(id,name,accountId)
CREATE INDEX IF NOT EXISTS CommunicationIndexTable ON Communications(id,phone,accountId)
CREATE INDEX IF NOT EXISTS OptionalInfoIndexTable ON CustomerOptionalInfos(id,customerType,accountId)
and then i used join to get data. Before that I have questions
1: Do I need to use the indexed table in my join?
the Join query:
SELECT Accounts.accountId AS accountID,
Accounts.roleId AS roleID,
Accounts.email AS email,
Accounts.firstName || " " || Accounts.lastName AS name,
Addresses.street AS street,
Addresses.city AS city,
Addresses.state AS state,
Addresses.country AS country,
Addresses.pincode AS pincode,
Communications.phone AS phone,
Companies.name AS compName,
CustomerOptionalInfos.customerType AS cType
FROM Accounts
LEFT JOIN Addresses ON Addresses.accountId=Accounts.accountId
AND Addresses.addressTypeId=1
LEFT JOIN Communications ON Communications.accountId=Accounts.accountId
LEFT JOIN Companies ON Companies.accountId=Accounts.accountId
LEFT JOIN CustomerOptionalInfos ON CustomerOptionalInfos.accountId=Accounts.accountId
WHERE 1=1
AND Accounts.isDelete!='true'
AND Accounts.firstName!= ''
AND Accounts.roleId = 5
ORDER BY Accounts.firstName ASC
LIMIT ?,?
It still takes +2 seconds to execute. Please suggest me the better way of doing this. I have a large number of data.

How to reuse a table with UNION?

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;

How can WHERE constraints be passed to a count(...) function in an inner SELECT query in a VIEW?

I have a VIEW in SQLite which is, schematically, like this:
SELECT ownerid, field, origin, count FROM (
SELECT
null AS ownerid,
table1.somefield AS field,
0 AS origin,
count(table2.ownerid) AS count
FROM table1 LEFT JOIN table2
UNION ALL
SELECT
table2.ownerid AS ownerid,
table2.someotherfield AS field,
1 AS origin,
1 AS count
)
I'm calling this with a SELECT FROM viewname WHERE ownerid=? OR origin=0 type query—in other words, what I want is entries from the first table, plus entries from the second table where the owner id is correct.
The count column for the first table is supposed to be the count of all rows in the second table that match the constraint—in other words, whose ownerid is the one selected for. What I currently get is the count for the entire table.
How can I constrain the expression inside the count(...) function call to match the query constraint being passed to the VIEW?
count(SELECT * FROM table2 WHERE ownerid=?)
what I think you really need though is to simplify your view and restrict the information after like so.
SELECT ownerid, field, origin, count FROM (
SELECT
null AS ownerid,
table1.somefield AS field,
0 AS origin
FROM table1 LEFT JOIN table2
UNION ALL
SELECT
table2.ownerid AS ownerid,
table2.someotherfield AS field,
1 AS origin
)
and then get the required counts and such from it like this
SELECT *, CASE WHEN viewname.ownerid IS NULL THEN COUNT(select * from table2 where ownerid=?) ELSE 1 END
FROM viewname

Creating a VIEW from multiple tables each with a different number of columns

I want to combine multiple tables into one VIEW.
My understanding is that if the number of columns are different we cannot use UNION.
How do I solve this?
I have the following three TABLES:
1.Table Name- Albums
2.Table Name-AlbumPictures
3.Table Name-Stories
I want to have 3 tables as follows:(i can do this part using INNER JOINS- kindly correct me if i am wrong)
For Stories: StoryID,AlbumID,StoryTitle,AlbumCover,Votes
For Albums: AlbumID,AlbumName,AlbumCover,Votes
For Pictures: AlbumPictureID,Votes
I want to merge all the rows retrieved from the above queries into one VIEWand shuffle them. As the number of columns are different in each of the result sets above am I able to combine them into one VIEW?
So in your UNION sql, either remove the extra columns from the sql for the table with too many, or add extra columns with constant default values to the sql for the table with fewer columns.
Based on your example output, adding extra constant values might look like this...
Select StoryID id, AlbumID,
StoryTitle name, AlbumCover, Votes
From Stories
UNION
Select AlbumID id, AlbumID,
AlbumName name, AlbumCover, Votes
From Albums
UNION
Select AlbumPictureID id, null AlbumId,
null AlbumCover, Votes
From pictures
Order By id, Votes, name
But this makes me want to ask WHY???
EDIT: To sort, just add an order by using output column names, as shown above....
In order to use a UNION or UNION ALL operator, the number of columns and datatypes of the columns returned by each query have to be the same.
One trick you can use is to return a NULL value for the columns that are "missing" from some of the queries.
For performance, I recommend you use the UNION ALL operator in place of the UNION operator, if removing duplicates is not a requirement.
Whenever I need to do something like this, I usually include a literal in each query, as an identifier of which query the row came from.
e.g.
SELECT 'a' AS source
, a.id AS id
, a.name AS name
FROM table_a a
UNION ALL
SELECT 'b' AS source
, b.id AS id
, NULL AS name
FROM table_b b
ORDER BY 1,2
You can do something like this. All three tables are given similar columns with null values and TableName column is to identify the table which brings the data
EDIT: I have to say, this is not the right approach. I wanted to show you how to union tables but I think now it is getting ugly when editing it according to your comments.
--Note: Vote is on all three table, I have selected from Stories
select s.storyId, a.albumId, s.storyTitle, null albumName,
ap.albumCover, s.votes , null albumPictureId, 'stories-albums-albumPics' tableName
from Stories s join Albums a on s.albumId = a.albumId
join AlbumPictures ap on a.albumid = ap.albumId
UNION ALL
select null storyId, a.albumID, null storyTitle, a.albumName,
ap.albumCover, a.votes, null albumPictureId, 'albums-albumPics' tableName
from Albums a join AlbumPictures ap on a.albumid = ap.albumId
UNION ALL --use required table here as well
select null storyId, null albumId, null storyTitle, null albumName,
null albumCover, votes, albumPictureId, 'pictures' tableName
from Pictures
I guess this makes little sense,
Select StoryID+'SID' id, AlbumID,
StoryTitle name, AlbumCover, Votes
From Stories
UNION
Select AlbumID+'AID' id, AlbumID,
AlbumName name, AlbumCover, Votes
From Albums
UNION
Select AlbumPictureID+'APID' id, null AlbumId,
null AlbumCover, Votes
From pictures
Concatenating 'SID','AID' and 'APID' and it will make some sense when you see UI data
select * from Stories as s
inner join Albums as a on a.AccountID = s.AccountID
inner join Pictures as p on p.AccountID = s.AccountID
will return all, as long as AccountID is defined in all 3 tables
To only obtain the unique columns change * for the columns you desire
Why on earth would you need the data to be all in the same view? Just return 3 sets of data. If for example you are using a web browser as the front end, you could perform three queries and return them as a single set of JSON, for example:
{
Albums: [
{AlbumID: 1, AlbumName: 'blah', ... },
{AlbumID: 2, AlbumName: 'gorp', ... }
],
AlbumPictures: [
{AlbumID: 1, URL: 'http://fun.jpg'},
{AlbumID: 1, URL: 'http://fun2.jpg'}
],
Stories [
{StoryID: 3, StoryTitle: 'Jack & Jill', ... },
{ etc. }
]
}
There is absolutely no programming architectural constraint forcing you to put everything together in a single view.

Resources