Getting ranges of arbitrary strings in SQL based on sequence dictated in a separate table - sql

Consider the following dataset (may look weird but want to land my point that the strings are arbitrary):
Table A
TicketId
StartAnimal
EndAnimal
1
Monkey
Bee
1
Lion
Buffalo
Table B
Animal
Sequence
Monkey
1
Zebra
2
Bee
3
Turtle
4
Lion
5
Buffalo
6
Is it possible to retrieve the animals that correspond to Ticket ID 1 based on the different "ranges" in each of its rows? For example,for Ticket ID 1 the following animals should be retrieved: Monkey, Zebra, Bee, Lion, Buffalo.
As you can see the animal strings themselves have no order logic to it, but the sequence can be leveraged for it. I'm just failing to come up with how to reference it for each row in a single query.
Edit
As an edge case, sometimes the EndAnimal might not even have a sequence to start with, in which case only the StartAnimal should be returned. As an example, assuming Bee is not in the sequence table, we should only get Monkey, Lion and Buffalo. Is that something SQL can handle?
Thanks!

There are numerous ways, one such way is to inner join the tables to find the corresponding start and end sequences and then find those rows that qualify:
with s as (
select bs.Sequence s1, IsNull(be.sequence,1) s2, a.ticketId
from a left join b bs on bs.animal = a.StartAnimal
left join b be on be.Animal = a.EndAnimal
)
select b.Animal
from b
join s on b.Sequence >=s1 and b.Sequence <= s2
where s.ticketId = 1
order by b.Sequence;
Example Fiddle

Related

Replacing id's in 2 columns from same table with names from second table

I am trying to select all Song1 and Song2 rows from my Connections table but instead of showing the id number, I want to use my Songs table to replace the number with the Name of the song
This is the things I have tried and what I got:
select Name,Name
from Connections
JOIN Songs
ON Songs.SongID = CAST(Connections.Song1 AS varchar)
JOIN Connections as Connections2
ON Songs.SongID = CAST(Connections2.Song2 AS varchar);
^ This gives me 2 columns that are the same that only shows song 6 (because it is used in both song1 and song2)
select Name from Songs
JOIN Connections
ON Songs.SongID = connections.Song1
select Name from Songs
JOIN Connections
ON Songs.SongID = connections.Song2
^ The top one gives the desired outcome for the Song1 Column and the bottom one gives the desired outcome for the Song2 column but I can't figure out how to join them.
I am using Microsoft SQL Server Management Studio and new to SQL
Connections table:
Song1
Song2
1
2
3
4
5
6
6
7
Songs table:
SongId
Name
1
Turning Back
2
Reason
3
Solar System
4
Generator
5
Siren
6
Circles
7
Bunker
The desired output for the first 4 rows would be:
Song1
Song2
Turning Back
Reason
Solar System
Generator
Siren
Circles
Cirles
Bunker
You want two joins:
select s1.Name, s2.Name
from connections c join
songs s1
on s1.SongID = c.song1 join
songs s2
on s2.SongId = c.song2;
The following applies to the original question and was apparently a propos given the edits.
Your data model and query is a little confusing:
Why are you converting song1 to a string? That should not be necessary. Columns that link two tables should have the same type -- and be declared as foreign keys.
Why is a column called name stored as a number?
These questions don't need to be answered. They are just feedback on the design of the data.
The below query is for MySQL and I am sure this will work in MS SQL too. We are using nested query here, the output of nested query will give SongId to extract the name of the Songs from SongTable.
select SongTable.Name from SongTable
where SongId in (select Song1,Song2 from ConnectionTable where ConnectionId="?");

Joining two tables, one of which containing the other

I am new in the SQL world (my problem might be very easy to solve).
Also,I work exclusively on Access.
I'm french and I realise that it is hard for me to explain so I'll give you a demo after.
I have two tables, A and B.
Both have a column id and all of the id of B are in A (but B has different columns however)
I'd like to join them, based on the id of course, to obtain a table containing A plus B when there is a matching id.
Therefore result must have the exact number of rows as table A.
Table A
id - value1 - value2
1 0.5 0.6
2 0.4 0.9
3 0.4 1
4 0.8 1
Table B
id - name1 - name2
1 az at
4 df ml
Result:
id- value1- value2 - name1- name2
1 0.5 0.6 az at
2 0.4 0.9
3 0.4 1
4 0.8 1 df ml
When an element of A is not in B, I still need to have them in the result, with a null value in the columns where the information is missing.
If you don't understand my english, just look at the demo, it's self explanatory
I tried to code a FULL OUTER JOIN by myself (because it doesn't exist in Access) but the result I get is a table with a lot of duplicates.
I also tried a lot of JOINs but I always get a table with a lot more rows than A
Thanks in advance :)
I think you just need a left join here:
SELECT
a.id,
a.value1,
a.value2,
b.name1,
b.name2
FROM TableA a
LEFT JOIN TableB b
ON a.id = b.id;
A left join would guarantee that every row in TableA would appear in the result set, even if a given row's id value did not match to anything in TableB.
If you don't want NULL to possibly appear for the name1 and name2 fields for those records where there was no match, then consider using Nz(name1, '') to show empty string instead.

Postgresql: Values of multiple rows in one row

I have the following database:
Car: {[CarID, HorsePower, Brand, HeadDesigner]}
DesignsCar:{[CarID, DesID]}
Designer:{[DesID, Name]}
You should note that while every Car has only 1 HeadDesigner, multiple people can design cars (as in work on them).
Say I have 10 cars in my database. For CarID (1..9) only one DesID per CarID in DesignsCar.
However, for carID 10 we have 3 people working on it (carID has 3 entries in DesignsCar because 3 people worked on it).
Say I do this:
select *
from car c
left outer join designscar ds on c.carid = ds.carid
left outer join designer d on frb.persnr = r.persnr
This gives me 12 rows, when I only want 10. The reason why this gives me 12 rows should be clear: for carID 10 we have 3 people working on it (carID has 3 entries in DesignsCar because 3 people worked on it).
I hope I've done a good job explaining this problem, so here comes my question:
How do I modify the query above so I get 10 Rows. For CarID 10 I'd like the 3 designers to be written in one column (like, comma separated but anything works as long it's in one column).
Is that possible?
You need to aggregate the values. Here is one possibility:
select c.*,
array_agg(d.name) as designer_names
from car c left outer join
designscar ds
on c.carid = ds.carid left outer join
designer d
on frb.persnr = r.persnr
group by c.carid ; -- allowed assuming `carid` is the primary key

How to select group of distinct rows from the table

Let's assume, a table has the following rows
ID Name Value
1 Apple Red
1 Taste Sour
2 Apple Yellow
2 Taste Sweet
3 Apple Red
3 Taste Sour
4 Apple Green
4 Taste Tart
5 Apple Yellow
5 Taste Sweet
I wonder, how can I select ID's corresponding to distinct combination of Apple and Taste? For example, ID=1 corresponds to red sour apple and ID=3 can be omitted in the query result. Similarly, ID=2 is for yellow sweet apple and ID=5 can be excluded from the query result, etc. A valid query result can be any of the following ID sets: (1,2,4), (1,4,5), (2,3,4) etc.
The query or the model could be improved with more understanding of the problem.
But assuming the model is correct and the problem is presented as this, this would be my quick approach.
SELECT MIN(a.ID) as ID
FROM Table a
INNER JOIN Table b ON a.ID = b.ID AND a.Name > b.Name
GROUP BY a.Value, b.Value
This query is joining the table with itself using the ID. But because you would have four lines for each possible combination (Ex.: Apple-Apple, Taste-Taste, Apple-Taste and Taste-Apple), you need to state not only that they are different (Because you would still have Apple-Taste and Taste-Apple) but that one of them is bigger than the other (That way you choose to have Apples on one side of the join and Tastes in the other). That's why there is the a.Name > b.Name.
You then group by both the values, stating that you don't want to have more than one combination of Apple values and Taste values. Resulting in only three lines.
The Select I think it depends of the RDBMS (I used SQL Server syntax), and it's selecting the lowest ID. You don't care, so you could choose Min or Max. Min results in lines with 1,2,4. Max would result in 3,4,5.

Working with sets of rows in (My)SQL and comparing values

I am trying to figure out the SQL for doing some relatively simple operations on sets of records in a table but I am stuck. Consider a table with multiple rows per item, all identified by a common key.
For example:
serial model color
XX1 A blue
XX2 A blue
XX3 A green
XX5 B red
XX6 B blue
XX1 B blue
What I would for example want to do is:
Assuming that all model A rows must have the same color, find the rows which dont. (for example, XX3 is green).
Assuming that a given serial number can only point to a single type of model, find out the rows which that does not occur (for example XX1 points both to A and B)
These are all simple logically things to do. To abstract it, I want to know how to group things by using a single key (or combination of keys) and then compare the values of those records.
Should I use a join on the same table? should i use some sort of array or similar?
thanks for your help
For 1:
SELECT model, color, COUNT(*) AS num FROM yourTable GROUP BY model, color;
This will give you a list of each model and each color for that model along with the count. So the output from your dataset would be:
model color num
A blue 2
A green 1
B red 1
B blue 2
From this output you can easily see what's incorrect and fix it using an UPDATE statement or do a blanket operation where you assign the most popular color to each model.
For 2:
SELECT serial, COUNT(*) AS num FROM yourTable GROUP BY serial HAVING num > 1
The output for this would be:
serial num
XX1 2
To address #1, I would use a self-join (a join on the same table, as you put it).
For example,
select *
from mytable
where serial in (select serial
from mytable
group by model, color
having count(*) = 1)
would find all the serial numbers that only exist in one color. I did not test this, but I hope you see what it does. The inner select finds all the records that only occur once, then the outer select shows all detail for those serials.
Of course, having said that, this is a poor table design. But I don't think that was your question. And I hope this was a made up example for a real situation. My concern would be that there is no reason to assume that the single occurrence is actually bad -- it could be that there are 10 records, all of which have a distinct color. This approach would tell you that all of them are wrong, and you would be unable to decide which was correct.