I'd like to have a SELECT query that gets 8 specific combinations of user1 & user2 (and that combination of user2 & user1 is also worth having and not redundant). The "IN" statement seemed worthwhile.
Directly related questions.
1. Can "WHERE IN" statements be nested?
If not, how might I most effectively/ most easily structure a query so that I might specify (for 4 pairs where user1 and user2 switch position - 8 combinations): (where user1 = billy AND user2 = sue) OR (where user1 = sue AND user2 =billy) OR (user1 = jack AND user2 = jill) OR (user1 = jill AND user2 = jack)...etc ?
[At this point it seems easier to run the query and grep out the pertinent info.]
Current thought:
sqlite3 -header -separator , some.db "SELECT DISTINCT programquality.time, simscores.user1, simscores.user2, simscores.simscore, programquality.gf, programquality.ga, programquality.pq FROM simscores LEFT JOIN programquality ON programquality.time = simscores.time AND programquality.username = simscores.user1 WHERE programquality.pq IS NOT NULL WHERE simscores.user1 IN ("abraham","billy","carl","dave","ethan","frank","george","harry") WHERE simscores.user2 IN ("abraham","billy","carl","dave","ethan","frank","george","harry");"
I've used this, but some non-relevant data is displayed.
sqlite3 -header -separator , some.db 'SELECT DISTINCT programquality.time, simscores.user1, simscores.user2, simscores.simscore, programquality.gf, programquality.ga, programquality.pq FROM simscores LEFT JOIN programquality ON programquality.time = simscores.time AND programquality.username = simscores.user1 WHERE (simscores.user1 = "billy" OR simscores.user1 = "suzy" OR simscores.user1 = "john") AND (simscores.user2 = "billy" OR simscores.user2 = "suzy" OR simscores.user2 = "john") AND programquality.pq IS NOT NULL AND programquality.time IS NOT NULL;'
A query can have only one WHERE clause, but that expression can combine multiple conditions with AND or OR:
SELECT ...
FROM ...
WHERE programquality.pq IS NOT NULL AND
simscores.user1 IN ('abraham', 'billy', ...) AND
simscores.user2 IN ('abraham', 'billy', ...)
However, these IN expressions do not allow you to match specific values for user1 and user2.
You cannot avoid listing all combinations you want.
However, you can simplify the expression somewhat by check the combined names for valid combinations:
... WHERE ... AND
simscores.user1 || '+' || simscores.user2
IN ('billy+sue', 'sue+billy',
'jack+jill', 'jill+jack',
...)
Related
I want to intersect two or more results from a query from a for loop
My code takes in strings from the command line and checks the database for movies with matching actors
movie database
id | name
actor database
id | name
acting
movie_id | actor_id
i.e.
inp = sys.argv[1]
query =
'''
SELECT m.name
FROM movie m
JOIN acting ag on m.id = ag.movie_id
JOIN actor a on a.id = ag.actor_id
WHERE a.name = ?
'''
cur.execute(query, (inp,))
for tup in cur.fetchall:
print(tup)
This program would get all the movies with actor called David
./program "David'
The Secret Life of Pets
The Big Short
Concussion
A Most Violent Year
Baggage Claim
Skyfall
Drive
Albert Nobbs
The Book of Eli
Shine a Light
The Bourne Ultimatum
The Simpsons Movie
I want to extend my program to take in multiple arguments, taking in multiple names of actors, where the actors act in the same movie.
Possible code?
for index in range(len(sys.argv - 1)):
# insert code here
I think I should use an intersection of the outputs of the queries, but I don't know how to do that.
This is the output of movies that both Albert and David
./program "Albert" "David"
A Most Violent Year
Baggage Claim
The Simpsons Movie
You can use aggregation to construct a query that returns the common movies of all actors that you pass as arguments:
query = """
SELECT m.name
FROM movie m
JOIN acting ag on m.id = ag.movie_id
JOIN actor a on a.id = ag.actor_id
WHERE ',' || ? || ',' LIKE '%,' || a.name || ',%'
GROUP BY m.id, m.name
HAVING COUNT(*) = ?
"""
cur = con.cursor()
cur.execute(query, (','.join(sys.argv[1:]), len(sys.argv)-1))
for tup in cur.fetchall():
print(tup[0])
select distinct substr(value,2,4),summary
from ticket_custom
LEFT JOIN ticket On ticket_custom.substr(value,2,4) = ticket.id
where name = "parent" AND value <> 0
I have a table ticket_custom where the ticket-number has a hastag in front of the ticketnumber (e.g. "#4567"), but in the table ticket, the ticketnumber is saved without this hashtag ("4567").
I would like to left join these two tables and would like to use the "summary"-value of the table ticket right next to the ticket-number without the #. I tried the above query, but I am getting an error. How can I combine substr = using the ticketnumber without the # with a left join? Or is there another way to do this?
Thanks!
You should be doing substr(ticket_custom.value,2) instead of ticket_custom.substr(value,2,4) (syntax was incorrect, and string length isn't guaranteed only to be 5 chars)
So your query should look like:
select distinct substr(value,2),summary
from ticket_custom
LEFT JOIN ticket On substr(ticket_custom.value,2) = ticket.id
where name = "parent" AND value <> 0
This is the correct syntax for the function substr():
On substr(ticket_custom.value,2,4) = ticket.id
or
On substr(ticket_custom.value,2) = ticket.id
if ticket_custom.value contains only 5 characters.
In this case (only 5 characters) you can use replace():
On replace(ticket_custom.value, '#', '') = ticket.id
or simply:
On ticket_custom.value = '#' || ticket.id
I'm trying to replace a placeholder string inside a selection of 10 random records with a random string (a name) taken from another table, using only sqlite statements.
i've done a subquery in order to replace() of the placeholder with the results of a subquery. I thought that each subquery loaded a random name from the names table, but i've found that it's not the case and each placeholder is replaced with the same string.
select id, (replace (snippet, "%NAME%", (select
name from names
where gender = "male"
) )
) as snippet
from imagedata
where timestamp is not NULL
order by random()
limit 10
I was expecting for each row of the SELECT to have different random replacement every time the subquery is invoked.
hello i'm %NAME% and this is my house
This is the car of %NAME%, let me know what you think
instead each row has the same kind of replacement:
hello i'm david and this is my house
This is the car of david, let me know what you think
and so on...
I'm not sure it can be done inside sqlite or if i have to do it in php over two different database queries.
Thanks in advance!
Seems that random() in the subquery is only evaluated once.
Try this:
select
i.id,
replace(i.snippet, '%NAME%', n.name) snippet
from (
select
id,
snippet,
abs(random()) % (select count(*) from names where gender = 'male') + 1 num
from imagedata
where timestamp is not NULL
order by random() limit 10
) i inner join (
select
n.name,
(select count(*) from names where name < n.name and gender = 'male') + 1 num
from names n
where gender = 'male'
) n on n.num = i.num
I did some tests on my db (SQLite 3.21.0) and its seems that my solution works, but I am afraid that its just luck and it may fail in some cases, I would like to know if my solution is valid...
It seems like GROUP_CONCAT() using primary key or if there is no primary key it will try to find numeric column and will use it for the concatenation order, so I wanted to check my theory and decided to use WITH clause to generate "new" table, this way I will set the order that GROUP_CONCAT will have to use
Here is what I did: (simplified query)
WITH data AS (SELECT cID,
pType || ':' || pID || ':' || pTotal || ':' ||
(CASE WHEN pDate IS NULL OR pDate = '' THEN '0' ELSE pDate END) fees
FROM Pay2015 AS A WHERE cID = A.cID AND pType > 1 ORDER BY pType)
SELECT A.cID, GROUP_CONCAT(data.fees, '|') fees FROM Pay2015 AS A
LEFT OUTER JOIN data ON A.cID = data.cID
WHERE A.cID = 98 AND pType = 0
WITH data will have table with first column as cID and it will be 98 for every row, it will be used in join, GROUP_CONCAT will also have to use it because its the only numeric column, and ORDER BY will set rows order that I need
Now the main query will execute GROUP_CONCAT on this new table (data) and because data.cID is the same for every row it will concatenate it as it
For comparison here is regular query that has the order problem:
SELECT cID, GROUP_CONCAT(pType || ':' || pID || ':' || pTotal || ':' ||
(CASE WHEN pDate IS NULL OR pDate = '' THEN '0' ELSE pDate END), '|') fees
FROM Pay2015 AS A WHERE cID = 98 AND pType > 1 ORDER BY pType
Pay2015 table data: (pID is primary key)
And this is the results when ordering by pType: (I use | to split by, removed all columns but pType for simplicity)
As you can see, the results order is the same as pID order in regular query
What do you think?
The implementation of group_concat() processes the rows in whatever order the database happens to read them.
The only way to enforce an order is to read the rows from a subquery (or view, or CTE) that actually has a specified order:
SELECT ... group_concat(...) ... FROM (SELECT ... ORDER BY ...);
Please note that that subquery must be the only data source in the FROM clause; if you are joining it with any other table, the join might cause the database to read rows in some other order. If you need to join, you must do it inside the subquery.
I have a query that Counts 2 columns from 2 separate tables using subqueries, which works. Now I have to implement into this query the ability to filter out these results based on the Date of a Call Record. I will post the query in which I am working with:
SELECT (m.FirstName || " " || m.LastName) AS Members,
(
SELECT count(CallToLineOfficers.MemberID)
FROM CallToLineOfficers
WHERE CallToLineOfficers.MemberID = m.MemberID
)
+ (
SELECT count(CallToMembers.MemberID)
FROM CallToMembers
WHERE CallToMembers.MemberID = m.MemberID
) AS Tally
FROM Members AS m, Call, CallToMembers, CallToLineOfficers
Join Call on CallToMembers.CallID = Call.CallID
and CallToLineOfficers.CallID = Call.CallI
WHERE m.FirstName <> 'None'
-- and Call.Date between '2017-03-21' and '2017-03-22'
GROUP BY m.MemberID
ORDER BY m.LastName ASC;
Ok, so table Call stores the Date and its PK is CallID. Both CallToLineOfficers and CallToMembers are Bridge Tables that also contain only CallID and MemberID. With the current query, where the Date is commented out, that Date range should only return all names, but a count of 1 should appear under 1 person's name.
I have tried joining Call.CallID with both Bridge Tables' CallIDs without any luck, though I think this is the right way to do it. Could someone help point me in the right direction? I am lost. (I tried explaining this the best I could, so if you need more info, let me know.)
UPDATED: Here is a screenshot of what I am getting:
Based on the provided date in the sample, the new results, with the Date, should be:
Bob Clark - 1
Rob Catalano - 1
Matt Butler - 1
Danielle Davidson - 1
Jerry Chuska - 1
Tom Cramer - 1
Everyone else should be 0.
At the moment, the subqueries filter only on the member ID. So for any member ID in the outer query, they return the full count.
To reduce the count, you have to filter in the subqueries:
SELECT (FirstName || " " || LastName) AS Members,
(
SELECT count(*)
FROM CallToLineOfficers
JOIN Call USING (CallID)
WHERE MemberID = m.MemberID
AND Date BETWEEN '2017-03-21' AND '2017-03-22'
)
+ (
SELECT count(*)
FROM CallToMembers
JOIN Call USING (CallID)
WHERE MemberID = m.MemberID
AND Date BETWEEN '2017-03-21' AND '2017-03-22'
) AS Tally
FROM Members AS m
WHERE FirstName <> 'None'
ORDER BY LastName ASC;