A CTE with DENSE_RANK, should i use a Subselect within OR a JOIN Condition - tsql

In the original table there are a lot of records. Is it better to filter within the CTE (example B) or should this be done in the JOIN condition (example A). Or is it possibly all the same, both are equally good/fast?
I guess in the CTE the whole table would have to be prefiltered first and in the JOIN only the corresponding records would be affected.
DECLARE #Orders TABLE(
orderid int NOT NULL,
orderdate datetime NOT NULL,
empid int NOT NULL,
custid varchar(5) NOT NULL,
qty int NOT NULL
);
-- in original lots of data
INSERT INTO #Orders(orderid, orderdate, empid, custid, qty) VALUES(30001, '20020802', 3, 'A', 10);
INSERT INTO #Orders(orderid, orderdate, empid, custid, qty) VALUES(10001, '20021224', 1, 'A', 12);
INSERT INTO #Orders(orderid, orderdate, empid, custid, qty) VALUES(10005, '20021224', 1, 'B', 20);
INSERT INTO #Orders(orderid, orderdate, empid, custid, qty) VALUES(40001, '20030109', 4, 'A', 40);
INSERT INTO #Orders(orderid, orderdate, empid, custid, qty) VALUES(10006, '20030118', 1, 'C', 14);
INSERT INTO #Orders(orderid, orderdate, empid, custid, qty) VALUES(20001, '20030212', 2, 'B', 12);
INSERT INTO #Orders(orderid, orderdate, empid, custid, qty) VALUES(40005, '20040212', 4, 'A', 10);
INSERT INTO #Orders(orderid, orderdate, empid, custid, qty) VALUES(20002, '20040216', 2, 'C', 20);
INSERT INTO #Orders(orderid, orderdate, empid, custid, qty) VALUES(30003, '20040418', 3, 'B', 15);
INSERT INTO #Orders(orderid, orderdate, empid, custid, qty) VALUES(30004, '20020418', 3, 'C', 22);
INSERT INTO #Orders(orderid, orderdate, empid, custid, qty) VALUES(30007, '20020907', 3, 'D', 30);
-- example A
WITH CTE_
AS(
SELECT
orderid
,orderdate
,empid
,custid
,qty
,DENSE_RANK() OVER(PARTITION BY custid ORDER BY orderid) AS [Ranking]
FROM #Orders
)
SELECT
d.*
FROM CTE_ AS d
WHERE d.[Ranking] = 1;
-------------------------------------
-- example B
WITH CTE_
AS(
SELECT
orderid
,orderdate
,empid
,custid
,qty
FROM(
SELECT
orderid
,orderdate
,empid
,custid
,qty
,DENSE_RANK() OVER(PARTITION BY custid ORDER BY orderid) AS [Ranking]
FROM #Orders
) AS d
WHERE d.[Ranking] = 1
)
SELECT
d.*
FROM CTE_ AS d

Generally, it's better to filter within the CTE (example B), especially in case of your tables have indexes in filter conditions, these indexes could be applied to be pre-filtered data first. Since a CTE is a temporary view, it can't have indexes, so in case of example A, your main query could not use indexes (if have) for filter condition in WHERE statement.

Related

PostgreSQL WITH RECURSIVE order by in non recursive term

I am trying to create a recursive CTE and I wanted to fetch the row in the non recursive term from the table using ORDER BY but it seems impossible to do. Is there any workaround on this?
Example:
CREATE TABLE mytable (
id BIGSERIAL PRIMARY KEY,
ref_id BIGINT NOT NULL,
previous_id BIGINT REFERENCES mytable(id),
some_name TEXT NOT NULL,
created_at TIMESTAMP DEFAULT NOW()
);
INSERT INTO mytable (id, previous_id, ref_id, some_name) VALUES (1, NULL, 1, 'Barry');
INSERT INTO mytable (id, previous_id, ref_id, some_name) VALUES (2, NULL, 1, 'Nick');
INSERT INTO mytable (id, previous_id, ref_id, some_name) VALUES (3, 1, 2, 'Janet');
INSERT INTO mytable (id, previous_id, ref_id, some_name) VALUES (4, 1, 1, 'John');
INSERT INTO mytable (id, previous_id, ref_id, some_name) VALUES (5, 2, 7, 'Ron');
INSERT INTO mytable (id, previous_id, ref_id, some_name) VALUES (6, 1, 1, 'Aaron');
INSERT INTO mytable (id, previous_id, ref_id, some_name) VALUES (7, 4, 1, 'Anna');
The query I am trying to construct
WITH RECURSIVE my_path AS (
SELECT * FROM mytable
WHERE ref_id = 1 AND some_name = 'Anna'
ORDER BY created_at DESC
LIMIT 1
UNION ALL
SELECT ph.* FROM my_path hp
INNER JOIN mytable ph ON hp.previous_id = ph.id
)
SELECT * FROM my_path;
SQLFIDDLE
Just move it into a starter CTE:
updated fiddle
WITH RECURSIVE base_record as (
SELECT * FROM mytable
WHERE ref_id = 1 AND some_name = 'Anna'
ORDER BY created_at DESC
LIMIT 1
), my_path AS (
SELECT * FROM base_record
UNION ALL
SELECT ph.* FROM my_path hp
INNER JOIN mytable ph ON hp.previous_id = ph.id
)
SELECT * FROM my_path;

Assigning new group for every n records in each category

I need to create groups for every n records in each categories. For example I have student table with StdId, Gender and Subject, now I want to divide all students in groups by their gender and subject and each group can not have more than two.
Here is code for sample data
declare #Students table (StdId int, Gender char(1), Subj varchar(10))
insert into #students select 1, 'F', 'Math'
insert into #students select 2, 'M', 'Math'
insert into #students select 3, 'M', 'Math'
insert into #students select 4, 'F', 'Math'
insert into #students select 5, 'F', 'Math'
insert into #students select 6, 'F', 'History'
insert into #students select 7, 'M', 'History'
insert into #students select 8, 'F', 'English'
insert into #students select 9, 'F', 'English'
insert into #students select 10, 'M', 'English'
insert into #students select 11, 'F', 'English'
insert into #students select 12, 'M', 'English'
And I need output like this:
GroupNo Subj Gender StdId
1 English F 8
1 English F 9
2 English F 11
3 English M 10
3 English M 12
4 History F 6
5 History M 7
6 Math F 1
6 Math F 4
7 Math F 5
8 Math M 2
8 Math M 3
Ok found my solution with the help of this
;with FirstRank as
(select DENSE_RANK() over (order by Subj, Gender) as rnk, * from #students),
SecondRank AS
(select (ROW_NUMBER() OVER (PARTITION BY rnk ORDER by gender)-1)
/ 2 as rn,*
from FirstRank
)
select DENSE_RANK() OVER (ORDER BY rnk,rn) as GrpNo, Subj, Gender, StdId from SecondRank

Order rows in a sequence and fill gaps for missing rows

I got a problem regarding missing rows in a table that is giving me a headache.
As base data, I have the following table:
declare #table table
(
id1 int,
id2 int,
ch char(1) not null,
val int
)
insert into #table values (1112, 121, 'A', 12)
insert into #table values (1351, 121, 'A', 13)
insert into #table values (1411, 121, 'B', 81)
insert into #table values (1312, 7, 'C', 107)
insert into #table values (1401, 2, 'A', 107)
insert into #table values (1454, 2, 'D', 107)
insert into #table values (1257, 6, 'A', 1)
insert into #table values (1269, 6, 'B', 12)
insert into #table values (1335, 6, 'C', 12)
insert into #table values (1341, 6, 'D', 5)
insert into #table values (1380, 6, 'A', 3)
The output should be ordered by id2 and follow a fixed sequence of ch, which should repeat until next id2 begins.
Sequence:
'A'
'B'
'C'
'D'
If the sequence or the pattern is interrupted, it should fill the missing rows with null, so that i get this result table:
id1 id2 ch val
----------------------------
1112 121 'A' 12
NULL 121 'B' NULL
NULL 121 'C' NULL
NULL 121 'D' NULL
1351 121 'A' 13
1411 121 'B' 81
NULL 121 'C' NULL
NULL 121 'D' NULL
NULL 7 'A' NULL
NULL 7 'B' NULL
1312 7 'C' 107
NULL 7 'D' NULL
1401 2 'A' 107
NULL 2 'B' NULL
NULL 2 'C' NULL
1454 2 'D' 107
and so on...
What I'm looking for is a way to do this without iterations.
I hope someone can help!
Thanks in advance!
A solution might be this:
declare #table table ( id1 int, id2 int, ch char(1) not null, val int )
insert into #table values (1112, 121, 'A', 12)
,(1351, 121, 'A', 13),(1411, 121, 'B', 81),(1312, 7, 'C', 107),(1401, 2, 'A', 107)
,(1454, 2, 'D', 107),(1257, 6, 'A', 1),(1269, 6, 'B', 12),(1335, 6, 'C', 12)
,(1341, 6, 'D', 5),(1380, 6, 'A', 3)
;with foo as
(select
*
,row_number() over (partition by id2 order by id1) rwn
,ascii(isnull(lag(ch,1) over (partition by id2 order by id1),'A'))-ascii('A') prev
,count(*) over (partition by id2,ch) nr
,ascii(ch)-ascii('A') cur
from #table
)
,bar as
(
select
*,case when cur<=prev and rwn>1 then 4 else 0 end + cur-prev step
from foo
)
,foobar as
(
select *,sum(step) over (partition by id2 order by id1 rows unbounded preceding) rownum
from bar
)
,iterations as
(
select id2,max(nr) nr from foo
group by id2
)
,blanks as
(
select
id2,ch chnr,char(ch+ascii('A') )ch,ROW_NUMBER() over (partition by id2 order by c.nr,ch)-1 rownum,c.nr
from iterations a
inner join (values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10)) c(nr)
on c.nr<=a.nr
cross join (values (0),(1),(2),(3)) b(ch)
)
select
b.id1,a.id2,a.ch,b.val
from blanks a
left join foobar b
on a.id2=b.id2 and a.rownum=b.rownum
order by a.id2,a.rownum
I first make the query "foo" which looks at the row number and gets the previous value for ch for each id2.
"bar" then finds how many missing values there are between the rows. For instance If the previous was an A and the current is a c then there are 2. If the previous was an A and the current is an A, then there are 4!
"foobar" then adds the steps, thus numbering the original rows, where they should be in the final output.
"iterations" counts the number of times the "ABCD" rows should appear.
"BLANKS" then is all the final rows, that is for each id2, it outputs all the "ABCD" rows that should be in the final output, and numbers them in rownum
Finally I left join "foobar" with "BLANKS" on id2 and rownum. Thus we get the correct number of rows, and the places where there are values in the original is output.
If you can manage to add an extra column in your table, that defines which [id2] are part from the same sequence you can try this:
declare #table table
(
id1 int,
id2 int,
ch char(1) not null,
val int,
category int -- extra column
)
insert into #table values (1112, 121, 'A', 12, 1)
insert into #table values (1351, 121, 'A', 13, 2)
insert into #table values (1411, 121, 'B', 81, 2)
insert into #table values (1312, 7, 'C', 107, 3)
insert into #table values (1401, 2, 'A', 107, 4)
insert into #table values (1454, 2, 'D', 107, 4)
insert into #table values (1257, 6, 'A', 1, 5)
insert into #table values (1269, 6, 'B', 12, 5)
insert into #table values (1335, 6, 'C', 12, 5)
insert into #table values (1341, 6, 'D', 5, 5)
insert into #table values (1380, 6, 'A', 3, 5)
DECLARE #sequence table (seq varchar(1))
INSERT INTO #sequence values ('A'), ('B'), ('C'), ('D')
SELECT b.id1, a.id2, a.seq, b.val, a.category
INTO #T1
FROM (
SELECT *
FROM #table
CROSS JOIN #sequence
) A
LEFT JOIN (
SELECT * FROM #table
) B
ON 1=1
AND a.id1 = b.id1
AND a.id2 = b.id2
AND a.seq = b.ch
AND a.val = b.val
;WITH rem_duplicates AS (
SELECT *, dup = ROW_NUMBER() OVER (PARTITION by id2, seq, category ORDER BY id1 DESC)
FROM #T1
) DELETE FROM rem_duplicates WHERE dup > 1
SELECT * FROM #T1 ORDER BY id2 DESC, category ASC, seq ASC
DROP TABLE #T1
I'm little confused by your output, try this:
Update
DECLARE #table TABLE
(
row INT IDENTITY(1, 1) ,
id1 INT ,
id2 INT ,
ch CHAR(1) NOT NULL ,
val INT
);
DECLARE #Sequence TABLE ( ch3 CHAR(1) NOT NULL );
INSERT INTO #Sequence
VALUES ( 'A' );
INSERT INTO #Sequence
VALUES ( 'B' );
INSERT INTO #Sequence
VALUES ( 'C' );
INSERT INTO #Sequence
VALUES ( 'D' );
INSERT INTO #table
VALUES ( 1112, 121, 'A', 12 );
INSERT INTO #table
VALUES ( 1351, 121, 'A', 13 );
INSERT INTO #table
VALUES ( 1411, 121, 'B', 81 );
INSERT INTO #table
VALUES ( 1312, 7, 'C', 107 );
INSERT INTO #table
VALUES ( 1401, 2, 'A', 107 );
INSERT INTO #table
VALUES ( 1454, 2, 'D', 107 );
INSERT INTO #table
VALUES ( 1257, 6, 'A', 1 );
INSERT INTO #table
VALUES ( 1269, 6, 'B', 12 );
INSERT INTO #table
VALUES ( 1335, 6, 'C', 12 );
INSERT INTO #table
VALUES ( 1341, 6, 'D', 5 );
INSERT INTO #table
VALUES ( 1380, 6, 'A', 3 );
SELECT r.id1 ,
fin.id2 ,
ch3 ,
r.val
FROM ( SELECT *
FROM ( SELECT CASE WHEN r.chd - l.chd = 1 THEN 0
ELSE 1
END [gap in sq] ,
l.*
FROM ( SELECT id2 ,
ASCII(ch) chd ,
ch ,
val ,
id1 ,
row
FROM #table
) AS l
LEFT JOIN ( SELECT id2 ,
ASCII(ch) chd ,
row
FROM #table
) AS r ON l.row = r.row - 1
) AS temp ,
#Sequence s
WHERE temp.[gap in sq] = 1
OR ( temp.[gap in sq] = 0
AND s.ch3 = temp.ch
)
) AS fin
LEFT JOIN #table r ON r.id2 = fin.id2
AND r.id1 = fin.id1
AND r.ch = fin.ch3

Select count from second table based on initial select

Table 1:
AccountId, ReferenceId, Name, (lots of other columns)
Table 2:
AccountId, ReferenceId, (other columns)
How can I do a select to get the following:
AccountId, ReferenceId, [Count(*) in Table2 where accountId and reference ID match.]
1, AB, 1
1, AC, 0
2, AD, 4
2, EF, 0
etc
Guessing a join, but that gives me values, not a count?
Tried adding a count, but get errors?
SELECT T1.AccountId,
T1.ReferenceId,
COUNT(T2.ReferenceId) AS Cnt
FROM Table1 T1
LEFT JOIN Table2 T2
ON T1.AccountId = T2.AccountId
AND T1.ReferenceId = T2.ReferenceId
GROUP BY T1.AccountId,
T1.ReferenceId
Something like:
SELECT t1.AccountId, t1.ReferenceId, COUNT(t2.AccountId)
FROM Table1 t1
LEFT JOIN Table2 t2 ON t1.AccountId = t2.AccountId AND
t1.ReferenceId = t2.ReferenceId
GROUP BY t1.AccountId, t1.ReferenceId
should work. The trick is to group by both key values so you can aggregate over other values. In this case you want to simply count values from other rows (you could also sum or average values from the grouped-by rows.).
sample data
declare #tbl1 table (AccountId INT, ReferenceId int, Name varchar(20))
declare #tbl2 table (AccountId INT, ReferenceId int)
insert into #tbl1 select 1, 10, 'White'
insert into #tbl1 select 2, 20, 'Green'
insert into #tbl1 select 3, 30, 'Black'
insert into #tbl1 select 3, 40, 'Red'
insert into #tbl2 select 1, 10
insert into #tbl2 select 1, 10
insert into #tbl2 select 2, 20
insert into #tbl2 select 3, 30
Query
select t.AccountId, t.ReferenceId, t.Name
,(select COUNT(*) from #tbl2 t2
where t.AccountId = t2.AccountId
and t.ReferenceId = t.ReferenceId) as countt
from #tbl1 t
SELECT t1.AccountId, t1.ReferenceId, COUNT(t2.AccountId)
FROM Table1 t1 LEFT JOIN Table2 t2
ON (t1.AccountId=t2.AccountId AND t1.ReferenceId=t2.ReferenceId)
GROUP BY Table1.AccountId, Table1.ReferenceId

Need help with a SELECT statement

I express the relationship between records and searchtags that can be attached to records like so:
TABLE RECORDS
id
name
TABLE SEARCHTAGS
id
recordid
name
I want to be able to SELECT records based on the searchtags that they have. For example, I want to be able to SELECT all records that have searchtags:
(1 OR 2 OR 5) AND (6 OR 7) AND (10)
Using the above data structure, I am uncertain how to structure the SQL to accomplish this.
Any suggestions?
Thanks!
You may want to try the following:
SELECT r.id, r.name
FROM records r
WHERE EXISTS (SELECT NULL FROM searchtags WHERE recordid = r.id AND id IN (1, 2, 5)) AND
EXISTS (SELECT NULL FROM searchtags WHERE recordid = r.id AND id IN (6, 7)) AND
EXISTS (SELECT NULL FROM searchtags WHERE recordid = r.id AND id IN (10));
Test case: Note that only records 1 and 4 will satisfy the query criteria.
CREATE TABLE records (id int, name varchar(10));
CREATE TABLE searchtags (id int, recordid int);
INSERT INTO records VALUES (1, 'a');
INSERT INTO records VALUES (2, 'b');
INSERT INTO records VALUES (3, 'c');
INSERT INTO records VALUES (4, 'd');
INSERT INTO searchtags VALUES (1, 1);
INSERT INTO searchtags VALUES (2, 1);
INSERT INTO searchtags VALUES (6, 1);
INSERT INTO searchtags VALUES (10, 1);
INSERT INTO searchtags VALUES (1, 2);
INSERT INTO searchtags VALUES (2, 2);
INSERT INTO searchtags VALUES (3, 2);
INSERT INTO searchtags VALUES (1, 3);
INSERT INTO searchtags VALUES (10, 3);
INSERT INTO searchtags VALUES (5, 4);
INSERT INTO searchtags VALUES (7, 4);
INSERT INTO searchtags VALUES (10, 4);
Result:
+------+------+
| id | name |
+------+------+
| 1 | a |
| 4 | d |
+------+------+
2 rows in set (0.01 sec)
SELECT
id, name
FROM
records
WHERE
EXISTS (
SELECT 1 FROM searchtags WHERE recordid = records.id AND id IN (1, 2, 5)
)
AND EXISTS (
SELECT 1 FROM searchtags WHERE recordid = records.id AND id IN (6, 7)
)
AND EXISTS (
SELECT 1 FROM searchtags WHERE recordid = records.id AND id IN (10)
)
not sure how to do it in mysql, but in t-sql, you could do something like:
SELECT id, name FROM RECORDS where id in (SELECT recordid from SEARCHTAGS where id in (1,2,5,6,7,10))
I may not be understanding your question entirely... but I gave it my best.
Try:
SELECT R.*
FROM RECORDS R, SEARCHTAGS S
WHERE R.id == S.recordid
AND S.name in (1,2,5,6,7,10);
Don't know if you need S.name or S.id, but this is an example.
select RECORDS.name
from RECORDS join SEARCHTAGS
on RECORDS.id = SEARCHTAGS.recordid
where RECORDS.id in (1,2,...)
I misread the question first time around, and thought it was asking for
(1 AND 2 AND 5) OR (6 AND 7) OR (10)
instead of the correct
(1 OR 2 OR 5) AND (6 OR 7) AND (10)
All the answers so far have concentrated on answering the specific example, rather than addressing the more general question "and suppose I want a different set of criteria next time".
Actual question
I can't do much better than the selected answer for the actual question (Query 1):
SELECT r.id, r.name
FROM Records AS r
WHERE EXISTS(SELECT * FROM SearchTags AS s
WHERE r.id = s.recordid AND s.id IN (1, 2, 5))
AND EXISTS(SELECT * FROM SearchTags AS s
WHERE r.id = s.recordid AND s.id IN (6, 7))
AND EXISTS(SELECT * FROM SearchTags AS s
WHERE r.id = s.recordid AND s.id IN (10));
This could be written as a join to 3 aliases for the SearchTags table.
Alternative question
There are several ways to answer the alternative - I think this is the most nearly neat and extensible. Clearly, the one item (10) is easy (Query 2):
SELECT r.id, r.name
FROM records AS r JOIN searchtags AS t ON r.id = t.recordid
WHERE t.id IN (10) -- or '= 10' but IN is consistent with what follows
The two items (6 or 7) can be done with (Query 3):
SELECT r.id, r.name
FROM records AS r JOIN searchtags AS t ON r.id = t.recordid
WHERE t.id IN (6, 7)
GROUP BY r.id, r.name
HAVING COUNT(*) = 2
The three items (1, 2, 5) can be done with (Query 4):
SELECT r.id, r.name
FROM records AS r JOIN searchtags AS t ON r.id = t.recordid
WHERE t.id IN (1, 2, 5)
GROUP BY r.id, r.name
HAVING COUNT(*) = 3
And the whole collection can be a UNION of the three terms.
Generalizing the solutions
The downside of this solution is that the SQL must be manually crafted for each set of items.
If you want to automate the 'SQL generation', you need the control data - the sets of interesting search tags - in a table:
CREATE TABLE InterestingTags(GroupID INTEGER, TagID INTEGER);
INSERT INTO InterestingTags(1, 1);
INSERT INTO InterestingTags(1, 2);
INSERT INTO InterestingTags(1, 5);
INSERT INTO InterestingTags(2, 6);
INSERT INTO InterestingTags(2, 7);
INSERT INTO InterestingTags(3, 10);
For the query asking for '(1 OR 2 OR 5) AND (...)' (conjunctive normal form), you can write (Query 5):
SELECT r.id, r.name
FROM records AS r JOIN
searchtags AS s ON r.id = s.recordID JOIN
interestingtags AS t ON s.id = t.tagID
GROUP BY r.id, r.name
HAVING COUNT(DISTINCT t.GroupID) = (SELECT COUNT(DISTINCT GroupID)
FROM InterestingTags);
This checks that the number of distinct 'interesting groups of tags' for a given record is equal to the total number of 'interesting groups of tags'.
For the query asking for '(1 AND 2 AND 5) OR (...)' (disjunctive normal form), you can write a join with InterestingTags and check that the Record has as many entries as the group of tags (Query 6):
SELECT i.id, i.name
FROM (SELECT q.id, q.name, c.GroupSize,
COUNT(DISTINCT t.GroupID) AS GroupCount
FROM records AS q JOIN
searchtags AS s ON q.id = s.recordID JOIN
interestingtags AS t ON s.id = t.tagID JOIN
(SELECT GroupID, COUNT(*) AS GroupSize
FROM InterestingTags
GROUP BY GroupID) AS c ON c.GroupID = t.GroupID
GROUP BY q.id, q.name, c.GroupSize
) AS i
WHERE i.GroupCount = i.GroupSize;
Test Data
I took the test data from Daniel Vassalo's answer and augmented it with some extra values:
CREATE TABLE records (id int, name varchar(10));
CREATE TABLE searchtags (id int, recordid int);
INSERT INTO records VALUES (1, 'a');
INSERT INTO records VALUES (2, 'b');
INSERT INTO records VALUES (3, 'c');
INSERT INTO records VALUES (4, 'd');
INSERT INTO records VALUES (11, 'A11');
INSERT INTO records VALUES (21, 'B12');
INSERT INTO records VALUES (31, 'C13');
INSERT INTO records VALUES (41, 'D14');
INSERT INTO records VALUES (51, 'E15');
INSERT INTO records VALUES (61, 'F16');
INSERT INTO searchtags VALUES (1, 1);
INSERT INTO searchtags VALUES (2, 1);
INSERT INTO searchtags VALUES (6, 1);
INSERT INTO searchtags VALUES (10, 1);
INSERT INTO searchtags VALUES (1, 2);
INSERT INTO searchtags VALUES (2, 2);
INSERT INTO searchtags VALUES (3, 2);
INSERT INTO searchtags VALUES (1, 3);
INSERT INTO searchtags VALUES (10, 3);
INSERT INTO searchtags VALUES (5, 4);
INSERT INTO searchtags VALUES (7, 4);
INSERT INTO searchtags VALUES (10, 4);
INSERT INTO searchtags VALUES (1, 11);
INSERT INTO searchtags VALUES (2, 11);
INSERT INTO searchtags VALUES (5, 11);
INSERT INTO searchtags VALUES (6, 21);
INSERT INTO searchtags VALUES (7, 21);
INSERT INTO searchtags VALUES (10, 31);
INSERT INTO searchtags VALUES (1, 41);
INSERT INTO searchtags VALUES (6, 41);
INSERT INTO searchtags VALUES (10, 41);
INSERT INTO searchtags VALUES (2, 51);
INSERT INTO searchtags VALUES (5, 51);
INSERT INTO searchtags VALUES (10, 51);
INSERT INTO searchtags VALUES (7, 61);
INSERT INTO searchtags VALUES (2, 61);
INSERT INTO searchtags VALUES (1, 61);
CREATE TABLE InterestingTags(GroupID INTEGER, TagID INTEGER);
INSERT INTO InterestingTags VALUES(1, 1);
INSERT INTO InterestingTags VALUES(1, 2);
INSERT INTO InterestingTags VALUES(1, 5);
INSERT INTO InterestingTags VALUES(2, 6);
INSERT INTO InterestingTags VALUES(2, 7);
INSERT INTO InterestingTags VALUES(3, 10);
Test results
The outputs that I got were:
Query 1
1 a
4 d
41 D14
Query 2
1 a
3 c
4 d
31 C13
41 D14
51 E15
Query 3
21 B12
Query 4
11 A11
Query 5
1 a
41 D14
4 d
Query 6
4 d
31 C13
3 c
1 a
41 D14
51 E15
Clearly, if I wanted the output in a specific order, I would add an ORDER BY clause to the queries.