list combinations in SQL Server 2000 - sql-server-2000

I have a table which lists combinations of profiles
id | p1 | p2
1 | 1 | 2
2 | 2 | 3
3 | 1 | 3
and a table which lists users, and adds a profile to it.
As you can see, a user can have multiple profiles
id | user | comb
1 | John | 1
2 | John | 3
3 | John | 2
4 | Jef | 1
5 | Jef | 2
Now, I'd like to see per user, the combinations that are equal to the first table.
excepted output:
| user | comb
| John | 1,2
| John | 1,3
| Jef| 2,3
How can I best do this in SQL Server 2000 (so no CTE :( )?
I can't get further than this:
select * from users where comb in (
select p2 from combinations c inner join users u on u.comb = c.p1
)

If I understand you right, you are looking for users that have an illegal combination of profiles. You can do that by starting from the illegal_combinations table:
SELECT *
FROM dbo.illegal_combinations ic
JOIN dbo.user_profile p1
ON ic.p1 = p1.comb
JOIN dbo.user_profile p2
ON ic.p2 = p2.comb
AND p1.user = p2.user;
That gives you for each combination all users, so you might end up with a user more than once. But that can be fixed easily:
SELECT DISTINCT p1.*
FROM dbo.illegal_combinations ic
JOIN dbo.user_profile p1
ON ic.p1 = p1.comb
JOIN dbo.user_profile p2
ON ic.p2 = p2.comb
AND p1.user = p2.user;
Instead of p1.* you should use a specific column list. You can include columns from one of the other tables, but then you might get duplicate users again.

Related

Oracle SQL query comparing multiple rows with same identifier

I'm honestly not sure how to title this - so apologies if it is unclear.
I have two tables I need to compare. One table contains tree names and nodes that belong to that tree. Each Tree_name/Tree_node combo will have its own line. For example:
Table: treenode
| TREE_NAME | TREE_NODE |
|-----------|-----------|
| 1 | A |
| 1 | B |
| 1 | C |
| 1 | D |
| 1 | E |
| 2 | A |
| 2 | B |
| 2 | D |
| 3 | C |
| 3 | D |
| 3 | E |
| 3 | F |
I have another table that contains names of queries and what tree_nodes they use. Example:
Table: queryrecord
| QUERY | TREE_NODE |
|---------|-----------|
| Alpha | A |
| Alpha | B |
| Alpha | D |
| BRAVO | A |
| BRAVO | B |
| BRAVO | D |
| CHARLIE | A |
| CHARLIE | B |
| CHARLIE | F |
I need to create an SQL where I input the QUERY name, and it returns any ‘TREE_NAME’ that includes all the nodes associated with the query. So if I input ‘ALPHA’, it would return TREE_NAME 1 & 2. If I ask it for CHARLIE, it would return nothing.
I only have read access, and don’t believe I can create temp tables, so I’m not sure if this is possible. Any advice would be amazing. Thank you!
You can use group by and having as follows:
Select t.tree_name
From tree_node t
join query_record q
on t.tree_node = q.tree_node
WHERE q.query = 'ALPHA'
Group by t.tree_name
Having count(distinct t.tree_node)
= (Select count(distinct q.tree_node) query_record q WHERE q.query = 'ALPHA');
Using an IN condition (a semi-join, which saves time over a join):
with prep (tree_node) as (select tree_node from queryrecord where query = :q)
select tree_name
from treenode
where tree_node in (select tree_node from prep)
group by tree_name
having count(*) = (select count(*) from prep)
;
:q in the prep subquery (in the with clause) is the bind variable to which you will assign the various QUERY values at runtime.
EDIT
I don't generally set up the test case on online engines; but in a comment below this answer, the OP said the query didn't work for him. So, I set up the example on SQLFiddle, here:
http://sqlfiddle.com/#!4/b575e/2
A couple of notes: for some reason, SQLFiddle thinks table names should be at most eight characters, so I had to change the second table name to queryrec (instead of queryrecord). I changed the name in the query, too, of course. And, second, I don't know how I can give bind values on SQLFiddle; I hard-coded the name 'Alpha'. (Note also that in the OP's sample data, this query value is not capitalized, while the other two are; of course, text values in SQL are case sensitive, so one should pay attention when testing.)
You can do this with a join and aggregation. The trick is to count the number of nodes in query_record before joining:
select qr.query, t.tree_name
from (select qr.*,
count(*) over (partition by query) as num_tree_node
from query_record qr
) qr join
tree_node t
on t.tree_node = qr.tree_node
where qr.query = 'ALPHA'
group by qr.query, t.tree_name, qr.num_tree_node
having count(*) = qr.num_tree_node;
Here is a db<>fiddle.

SQLite How do i join two tables

I have two tables that contains translations of different words in english and french
words : contains all the words used in the app
+----+-------+---------+--
| ID | TEXT | LANG_ID |
+----+-------+---------+--
| 1 | partir | 4 |
| 2 | manger | 4 |
| 3 | go | 5 |
| 4 | eat | 5 |
+----+-------+---------+--
Translated_word : contains the translations (english to french and vice versa)
+----+-------+---------+--
| ID | SOURCE | TO |
+----+-------+---------+--
| 10 | 1 | 3 |
| 12 | 2 | 4 |
| 13 | 3 | 1 |
| 14 | 4 | 2 |
+----+-------+---------+--
I need to get all the contents of the word table in this format
1 partir, go
2 manger, eat
3 go, partir
4 eat, manger
I have tried several queries and i am not able to achieve the desired results. For example, when i try this one
SELECT lp.id, tw.id, lp.text, tw.text FROM words AS lp JOIN words AS tw ON (tw.id = lp.id)
i get the following results
1 partir, partir
2 manger, manger
...
...
Can someone let me know what i am doing wrong ?
Thanks
This would use two joins:
select tw.id, w1.*, w2.*
from translated_word tw join
words w1
on tw.source = w1.id join
words w2
on tw.to = w2.id and w2.lang_id <> w1.lang_id
where lang_id in (4, 5);
Note the additional condition on the second join . . . the language ids are not equal. I might expect that you want English and French in different columns. For that:
select tw.id, w1.*, w2.*
from translated_word tw join
words wf
on tw.source = wf.id and wf.lang_id = 4 join
words we
on tw.to = we.id and we.lang_id = 5
where lang_id in (4, 5);
Join translated_word to 2 copies of words:
select w1.id,
w1.text || ', ' || w2.text
from words w1
inner join translated_word tw on tw.source = w1.id
inner join words w2 on w2.id = tw.`to`
See the demo.

Generating array-agg(...)s in postgres

Using Postgres 10 I have tables representing 'units', the 'group' to which each unit belongs, and the 'distance' between each pair of units.
I now want a group-to-group distance table that aggregates all the unit-to-unit distances for each pair of groups - i.e. an aggregate array of the distances from all the units in one group to all the units in the other group.
What I have
'units' 'unit_distances'
id | group this_unit_id | that_unit_id | distance
---------- --------------------------------------
1 | 1 1 | 2 | d_12
2 | 1 1 | 3 | d_13
3 | 2 1 | 4 | d_14
4 | 3 2 | 3 | d_23
... | ... ... | ... | ...
What I want
'group_distances'
this_group_id | that_group_id | all_distances
---------------------------------------------------
1 | 2 | {d_13, d_23}
1 | 3 | {d_14, d_24}
2 | 3 | {d_34}
... | ... | {..., ...
I'm not sure how to return all such arrays for all group-group pairings and generate the table depicted above. At the moment I can only get these arrays by defining the sites individually (as x and y below).
with dist as (
select distance
from unit_distances
inner join units
on unit_distances.this_unit_id = units.id
WHERE units.group = x
intersect
select distance
from unit_distances
inner join units
on unit_distances.that_unit_id = units.id
WHERE units.group = y)
select array_agg(distance) from dist;
Thanks
If I understand correctly, this is just two joins to get the groups and an aggregation for the distances:
select u1.group, u2.group, array_agg(ud.distance) as all_distances
from unit_distances ud join
units u1
on ud.this_unit_id = u1.id join
units u2
on ud.that_unit_id = u2.id
group by u1.group, u2.group;

1 to Many Query: Help Filtering Results

Problem: SQL Query that looks at the values in the "Many" relationship, and doesn't return values from the "1" relationship.
Tables Example: (this shows two different tables).
+---------------+----------------------------+-------+
| Unique Number | <-- Table 1 -- Table 2 --> | Roles |
+---------------+----------------------------+-------+
| 1 | | A |
| 2 | | B |
| 3 | | C |
| 4 | | D |
| 5 | | |
| 6 | | |
| 7 | | |
| 8 | | |
| 9 | | |
| 10 | | |
+---------------+----------------------------+-------+
When I run my query, I get multiple, unique numbers that show all of the roles associated to each number like so.
+---------------+-------+
| Unique Number | Roles |
+---------------+-------+
| 1 | C |
| 1 | D |
| 2 | A |
| 2 | B |
| 3 | A |
| 3 | B |
| 4 | C |
| 4 | A |
| 5 | B |
| 5 | C |
| 5 | D |
| 6 | D |
| 6 | A |
+---------------+-------+
I would like to be able to run my query and be able to say, "When the role of A is present, don't even show me the unique numbers that have the role of A".
Maybe if SQL could look at the roles and say, WHEN role A comes up, grab unique number and remove it from column 1.
Based on what I would "like" to happen (I put that in quotations as this might not even be possible) the following is what I would expect my query to return.
+---------------+-------+
| Unique Number | Roles |
+---------------+-------+
| 1 | C |
| 1 | D |
| 5 | B |
| 5 | C |
| 5 | D |
+---------------+-------+
UPDATE:
Query Example: I am querying 8 tables, but I condensed it to 4 for simplicity.
SELECT
c.UniqueNumber,
cp.pType,
p.pRole,
a.aRole
FROM c
JOIN cp ON cp.uniqueVal = c.uniqueVal
JOIN p ON p.uniqueVal = cp.uniqueVal
LEFT OUTER JOIN a.uniqueVal = p.uniqueVal
WHERE
--I do some basic filtering to get to the relevant clients data but nothing more than that.
ORDER BY
c.uniqueNumber
Table sizes: these tables can have anywhere from 50,000 rows to 500,000+
Pretending the table name is t and the column names are alpha and numb:
SELECT t.numb, t.alpha
FROM t
LEFT JOIN t AS s ON t.numb = s.numb
AND s.alpha = 'A'
WHERE s.numb IS NULL;
You can also do a subselect:
SELECT numb, alpha
FROM t
WHERE numb NOT IN (SELECT numb FROM t WHERE alpha = 'A');
Or one of the following if the subselect is materializing more than once (pick the one that is faster, ie, the one with the smaller subtable size):
SELECT t.numb, t.alpha
FROM t
JOIN (SELECT numb FROM t GROUP BY numb HAVING SUM(alpha = 'A') = 0) AS s USING (numb);
SELECT t.numb, t.alpha
FROM t
LEFT JOIN (SELECT numb FROM t GROUP BY numb HAVING SUM(alpha = 'A') > 0) AS s USING (numb)
WHERE s.numb IS NULL;
But the first one is probably faster and better[1]. Any of these methods can be folded into a larger query with multiple additional tables being joined in.
[1] Straight joins tend to be easier to read and faster to execute than queries involving subselects and the common exceptions are exceptionally rare for self-referential joins as they require a large mismatch in the size of the tables. You might hit those exceptions though, if the number of rows that reference the 'A' alpha value is exceptionally small and it is indexed properly.
There are many ways to do it, and the trade-offs depend on factors such as the size of the tables involved and what indexes are available. On general principles, my first instinct is to avoid a correlated subquery such as another, now-deleted answer proposed, but if the relationship table is small then it probably doesn't matter.
This version instead uses an uncorrelated subquery in the where clause, in conjunction with the not in operator:
select num, role
from one_to_many
where num not in (select otm2.num from one_to_many otm2 where otm2.role = 'A')
That form might be particularly effective if there are many rows in one_to_many, but only a small proportion have role A. Of course you can add an order by clause if the order in which result rows are returned is important.
There are also alternatives involving joining inline views or CTEs, and some of those might have advantages under particular circumstances.

SQL LEFT JOIN help

My scenario: There are 3 tables for storing tv show information; season, episode and episode_translation.
My data: There are 3 seasons, with 3 episodes each one, but there is only translation for one episode.
My objetive: I want to get a list of all the seasons and episodes for a show. If there is a translation available in a specified language, show it, otherwise show null.
My attempt to get serie 1 information in language 1:
SELECT
season_number AS season,number AS episode,name
FROM
season NATURAL JOIN episode
NATURAL LEFT JOIN episode_trans
WHERE
id_serie=1 AND
id_lang=1
ORDER BY
season_number,number
result:
+--------+---------+--------------------------------+
| season | episode | name |
+--------+---------+--------------------------------+
| 3 | 3 | Episode translated into lang 1 |
+--------+---------+--------------------------------+
expected result
+-----------------+--------------------------------+
| season | episode| name |
+-----------------+--------------------------------+
| 1 | 1 | NULL |
| 1 | 2 | NULL |
| 1 | 3 | NULL |
| 2 | 1 | NULL |
| 2 | 2 | NULL |
| 2 | 3 | NULL |
| 3 | 1 | NULL |
| 3 | 2 | NULL |
| 3 | 3 | Episode translated into lang 1 |
+--------+--------+--------------------------------+
Full DB dump
http://pastebin.com/Y8yXNHrH
I tested the following on MySQL 4.1 - it returns your expected output:
SELECT s.season_number AS season,
e.number AS episode,
et.name
FROM SEASON s
JOIN EPISODE e ON e.id_season = s.id_season
LEFT JOIN EPISODE_TRANS et ON et.id_episode = e.id_episode
AND et.id_lang = 1
WHERE s.id_serie = 1
ORDER BY s.season_number, e.number
Generally, when you use ANSI-92 JOIN syntax you need to specify the join criteria in the ON clause. In MySQL, I know that not providing it for INNER JOINs results in a cross join -- a cartesian product.
LEFT JOIN episode_trans
ON episode_trans.id_episode = episode.id_episode
AND episode_trans.id_lang = 1
WHERE id_serie=1
You probably need to move the id_lang = 1 into the LEFT JOIN clause instead of the WHERE clause. Think of it this way... for all of those rows with no translation the LEFT JOIN gives you back NULLs for all of those translation columns. Then in the WHERE clause you are checking to see if that is equal to 1 - which of course evaluates to FALSE.
It would probably be easier if you included your code in the question next time instead of in a link.
Can you try using
LEFT OUTER JOIN
instead of
NATURAL LEFT JOIN