SQLITE : inline views or nested subqueries? - sqlite

I'm trying to write a query on two simple tables. Tables are simple, the query is not :)
Anyway...
Here is the database scheme :
and here is an overview of table content :
I'm trying to write a query that would list all assets in corresponding table, only if the are marked as "wanted" (meaning the boolean field asset_owned =0) and that are referenced for another owner as "owned".
This is what I have so far and it works :
SELECT
user.user_pseudo AS REQUESTER,
asset.asset_sku AS SKU,
asset.asset_name AS ASSET_NAME
FROM
asset
INNER JOIN user ON asset.id_user = user.id
WHERE
asset.asset_owned = 0
AND
asset.asset_sku IN (SELECT asset.asset_sku FROM asset WHERE asset.asset_owned = 1)
But, in the same query (if possible) I would like to get the owner name as well.
The first result of such a query on those table would be :
me,003,Test003,you.
I've tried inline SELECT and nested subqueries like :
SELECT
user.user_pseudo as ASKER,
asset.asset_sku as SKU,
asset.asset_name as NAME,
subquery1.user.user_pseudo as OWNER
FROM
asset
INNER JOIN user ON asset.id_user = user.id,
(SELECT user.user_pseudo.asset_asset_sku FROM asset INNER JOIN user ON asset.id_user = user.id WHERE asset.asset_owned = 1) subquery1
WHERE
asset.asset_owned = 0 AND
subquery1.asset.asset_sku IN (SELECT asset.asset_sku FROM asset INNER JOIN user ON asset.id_user = user.id WHERE asset.asset_owned=1)
but of course that does not work.
Thanks for any direction you could point me to.
happy new year
Mathias

So this was fun for me (I'm learning SQL, so this is good practice!) - I appreciate the very clear question.
Hopefully this works for you - I used two sub-queries (one each for 'owner' and 'requester') and then joined those on SKU and name. It works in SQLite with the small sample data shown above.
SELECT requester, subq1.SKU, subq1.name, owner
FROM
(SELECT pseudo AS requester, SKU, name
FROM asset, user
WHERE owned = 0
AND user.id = id_user) subq1,
(SELECT pseudo AS owner, SKU, name
FROM asset, user
WHERE owned = 1
AND asset.id_user = user.id) subq2
WHERE subq1.SKU = subq2.SKU
AND subq1.name = subq2.name;

Related

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 do arithemtic operations with an alias in sqlite

I want to calculate with an alias in sqlite (Example is modified from http://www.sqlitetutorial.net):
if i do it like this, i get the error message "no such column: tracks_count"
SELECT albumid,
title,
(
SELECT count(trackid)
FROM tracks
WHERE tracks.AlbumId = albums.AlbumId
)
tracks_count, tracks_count * album_nr
FROM albums
ORDER BY tracks_count DESC;
if i do it like this, i get zero for the mulitplication
SELECT albumid,
title,
(
SELECT count(trackid)
FROM tracks
WHERE tracks.AlbumId = albums.AlbumId
)
tracks_count, "tracks_count" * album_nr
FROM albums
ORDER BY tracks_count DESC;
Table data for the example:
table albums
table tracks
You don't even need a subquery here:
SELECT
a.albumid,
a.title,
COUNT(t.albumid) AS tracks_count,
COUNT(t.albumid) * a.album_nr AS other_count
FROM albums a
LEFT JOIN tracks t
ON a.albumid = t.albumid
GROUP BY
a.albumid,
a.title;
If you wanted to make your current approach work, then the problem you are having is that you are referring to the tracks_count alias in the same select in which it was defined. This isn't allowed, because the alias may not have even been computed yet. But, I would recommend using the answer I gave above.

SQLite Group By Limit

I have a web service that generates radio station playlists and I'm trying to ensure that playlists never have tracks from the same artist more than n times.
So for example (unless it is Mandatory Metallica --haha) then no artist should ever dominate any 8 hour programming segment.
Today we use a query similar to this which generates smaller randomized playlists out of existing very large playlists:
SELECT FilePath FROM vwPlaylistTracks
WHERE Owner='{0}' COLLATE NOCASE AND
Playlist='{1}' COLLATE NOCASE
ORDER BY RANDOM()
LIMIT {2};
Someone then has to manually review the playlists and do some manual editing if the same artist appears consecutively or more than the desired limit.
Supposing the producer wants to ensure that no artist appears more than twice in the span of the playlist generated in this query (and assuming there is an artist field in the vwPlaylistTracks view; which there is) is GROUP BY the correct way to accomplish this?
I've been messing around with the view trying to accomplish this but this query always only returns 1 track from each artist.
SELECT
a.Name as 'Artist',
f.parentPath || '\' || f.fileName as 'FilePath',
p.name as 'Playlist',
u.username as 'Owner'
FROM mp3_file f,
mp3_track t,
mp3_artist a,
mp3_playlist_track pt,
mp3_playlist p,
mp3_user u
WHERE f.file_id = t.track_id
AND t.artist_id = a.artist_id
AND t.track_id = pt.track_id
AND pt.playlist_id = p.playlist_id
AND p.user_id = u.user_id
--AND p.Name = 'Alternative Rock'
GROUP BY a.Name
--HAVING Count(a.Name) < 3
--ORDER BY RANDOM()
--LIMIT 50;
GROUP BY creates exactly one result record for each distinct value in the grouped column, so this is not what you want.
You have to count any previous records with the same artist, which is not easy because the random ordering is not stable.
However, this is possible with a temporary table, which is ordered by its rowid:
CREATE TEMPORARY TABLE RandomTracks AS
SELECT a.Name as Artist, parentPath, name, username
FROM ...
WHERE ...
ORDER BY RANDOM();
CREATE INDEX RandomTracks_Artist on RandomTracks(Artist);
SELECT *
FROM RandomTracks AS r1
WHERE -- filter out if there are any two previous records with the same artist
(SELECT COUNT(*)
FROM RandomTracks AS r2
WHERE r2.Artist = r1.Artist
AND r2.rowid < r1.rowid
) < 2
AND -- filter out if the directly previous record has the same artist
r1.Artist IS NOT (SELECT Artist
FROM RandomTracks AS r3
WHERE r3.rowid = r1.rowid - 1)
LIMIT 50;
DROP TABLE RandomTracks;
It might be easier and faster to just read the entire playlist and to filter and reorder it in your code.

SELECT MAX() contained within a DATEDIFF()

I am writing a report for the desktop support team in the company where I work. The report needs to produce a set of new starters within a specified time frame passed in from an ASP.NET application. Currently there is a one to many relationship between our Worker table and Contract table. We hire a lot of contractors and they sometimes come back after a number of months but are still treated like new starters as new machines need to be configured along with desk space.
A new contract is added for every pay review, job title change and new starter. We need to filter out all but the new starter. The newest contract that is added for job changes and pay reviews is always one day after the end date of the previous contract naturally. As I am only still a fresher in the grand scheme of things I am struggling with a set of functions I am trying to use to achieve my goal.
WHERE
(dbo.[Contract].StartDate BETWEEN #StartDateF AND #EndDateF) AND DATEDIFF(day, SELECT MAX(StartDate)FROM dbo.[Contract] WHERE dbo.[Contract].Worker_ID = w1.Worker_ID, SELECT MAX(EndDate)FROM dbo.[Contract] WHERE dbo.[Contract].Worker_ID = w1.Worker_ID)> 1
I basically want to find out in the instance an employee has more than one contract, regardless of leaving and coming back or pay review, if the current active contract is one day different to the previous contract. This should by my thinking give me all new starters only.
Trouble is I am still trying to get my head around when to use aggregate functions not in a select and when to apply the HAVING clause.
Any help would be appreciated to help me understand why my lack of understanding is causing this query/logic to fail.
Thanks
EDIT
Ok I am still bashing away at this solution and this is syntactically incorrect. In an attempt to remove some of the ambiguity here is the query, with an update;
Declare #StartDateF varchar(10)
Set #StartDateF = '2012-08-03'
Declare #EndDateF varchar(10)
Set #EndDateF = '2012-09-04'
SELECT w1.Worker_ID, w1.Title, w1.FirstName, w1.Surname,w1.Gender, w1.DateofBirth,
dbo.[Contract].StartDate, (select w2.surname + ',' + w2.firstname from worker w2 WITH (NOLOCK) where w2.worker_ID = w1.manager)as Manager, dbo.Grade.GradeDescription AS JobTitle, dbo.Grade.Discipline,
CASE WHEN dbo.[Contract].ContractType_ID = 1 OR dbo.[Contract].ContractType_ID = 2 OR dbo.[Contract].ContractType_ID = 5 OR dbo.[Contract].ContractType_ID = 6
THEN 'Staff' ELSE 'Contractor' END AS ContractType
FROM dbo.Worker w1 WITH (NOLOCK) inner join
dbo.[Contract] WITH (NOLOCK) ON dbo.[Contract].Worker_ID = w1.Worker_ID inner join
dbo.Grade WITH (NOLOCK) ON dbo.Grade.Grade_ID = dbo.[Contract].Grade_ID
WHERE
(dbo.[Contract].StartDate BETWEEN #StartDateF AND #EndDateF AND EndDate IS NULL)
group by
w1.Worker_ID, w1.Title, w1.FirstName, w1.Surname,w1.Gender, w1.DateofBirth,
dbo.[Contract].StartDate, manager, dbo.Grade.Discipline,dbo.Grade.GradeDescription, dbo.[Contract].ContractType_ID
Having DATEDIFF(day, SELECT MAX(StartDate)FROM dbo.[Contract] WHERE dbo.[Contract].Worker_ID = w1.Worker_ID, SELECT MAX(EndDate)FROM dbo.[Contract] WHERE dbo.[Contract].Worker_ID = w1.Worker_ID)
I have added the group by and the having clause but now I am getting the following errors
Msg 156, Level 15, State 1, Line 24
Incorrect syntax near the keyword 'SELECT'.
Msg 102, Level 15, State 1, Line 24
Incorrect syntax near ','.
Msg 102, Level 15, State 1, Line 24
Incorrect syntax near ')'.
These all relate the the functions in the having clause no doubt you can see. But I cannot understand what is wrong with this query and this is mainly the question. I need to understand the SQL functions enough so that I can implement th correct solution.
I have followed up the DATEDIFF() function here http://msdn.microsoft.com/en-us/library/ms189794.aspx
I can see that using functions within this function is acceptable according to the MS documentation.
EDIT
Commenting out the Having clause gives me the result set I expect. It is showing people with changes to contracts(pay rise) but this is information that no one should be seeing, these are now the only records that need filtering out
EDIT
I have made some improvements and overcome the error messages now, but I am still getting people where pay rises have occured. Here is the amended query from the group by
group by
w1.Worker_ID, w1.Title, w1.FirstName, w1.Surname,w1.Gender, w1.DateofBirth,
dbo.[Contract].StartDate, manager, dbo.Grade.Discipline,dbo.Grade.GradeDescription, dbo.[Contract].ContractType_ID, w1.Worker_ID
Having
(((dbo.[Contract].StartDate BETWEEN #StartDateF AND #EndDateF)
AND COUNT(dbo.[Contract].Worker_ID) = 1)
OR
((dbo.[Contract].StartDate BETWEEN #StartDateF AND #EndDateF)
AND DATEDIFF(day, (SELECT MAX(EndDate)FROM dbo.[Contract] WHERE dbo.[Contract].Worker_ID = w1.Worker_ID), (SELECT MAX(StartDate)FROM dbo.[Contract] WHERE dbo.[Contract].Worker_ID = w1.Worker_ID))>1))
To get workers with more than one contract, you would use:
select c.workerID
from Contract c
group by c.workerID
having count(distinct contractID) > 1
It sounds, though, like you only want to count everything but the new start ones. You can do this with something like:
select w.workerID
from Contract c
where c.ContractType = 'New'
group by w.workerID
having count(distinct contractID) > 1
Because you didn't provide the details of what the tables look like, what sample input data looks like, and the results you want to achieve, this is about the best that can be done.
WHERE ( (dbo.[Contract].StartDate BETWEEN #StartDateF AND #EndDateF)AND dbo.[Contract].Worker_ID
IN (select worker_id from dbo.[Contract]
group by worker_id
having count(worker_id) = 1))
OR
((dbo.[Contract].StartDate BETWEEN #StartDateF AND #EndDateF)
AND DATEDIFF(day, (SELECT MAX(EndDate)FROM dbo.[Contract] WHERE dbo.[Contract].Worker_ID = w1.Worker_ID), (SELECT MAX(StartDate)FROM dbo.[Contract] WHERE dbo.[Contract].Worker_ID = w1.Worker_ID))>1
AND dbo.[Contract].Worker_ID = w1.Worker_ID )
Now works for me :)

Linq to Entities: Left join to get items NOT found in the join

I've got two un-related (no FK's defined) tables. The first table contains some tasks for which a user may not have access. I need to find all those tasks - in this case, the joined table would contain nulls. How do I get them?
Here's the setup:
TimeData table
- userID
- taskID
- hours
ApprovedTasks table (the one that should contain nulls)
- taskID
- userID
The SQL query would look like this:
select * from TimeData td
left join ApprovedTasks at
on at.taskID = td.taskID and at.userID = td.userID
where at.taskID is null
Any way to pull that off using a LINQ to Entity query?
TIA
Check out... Disjoint Union in LINQ
This should work...
var approvedTaks = from at in ApprovedTasks.Except(
from at2 in ApprovedTasks
where at2.userID == userId and at2.taskID==taskId
select at2)
where at.userID == userId and at.taskID==taskId
select at;
but sorry don't have the database handy to test it.

Resources