Joining tables on multiple conditions - sql

I have a little problem - since im not very experienced in SQL - about joining the same table on multiple values. Imagine there is table 1 (called Strings):
id value
1 value1
2 value2
and then there is table 2 (called Maps):
id name description
1 1 2
so name is reference into the Strings table, as is description. Without the second field referencing the Strings table it would be no problem, id just do an inner join on Strings.id = Maps.name. But now id like to obtain the actual string also for description. What would be the best approach for a SELECT that returns me both? Right now it looks like this:
SELECT Maps.id, Strings.value AS mapName FROM Maps INNER JOIN Strings ON Strings.id = Maps.name;
But that obviously only covers one of the localized names. Thank you in advance.

You can do this with two joins to the same table:
SELECT m.id, sname.value AS mapName, sdesc.value as description
FROM Maps m INNER JOIN
Strings sname
ON sname.id = m.name INNER JOIN
Strings desc
ON sdesc.id = m.description;
Note the use of table aliases to distinguish between the two tables.

As long as you want to get a single value from another table, you can use subqueries to do these lookups:
SELECT id,
(SELECT value FROM Strings WHERE id = Maps.name) AS name,
(SELECT value FROM Strings WHERE id = Maps.description) AS description
FROM Maps

Related

Get data from other table based on column with concatenated values

I have two tables:
category with columns:
id name
1 business
2 sports
...
article with columns:
id title categories
1 abc 1|2|3
2 xyz 1|2
I know there should be a separate table for article categories but I was given this.
Is it possible to write a query that returns:
id title category_names
1 xyz business,sports
I thought of splitting the string in article -> categories column, then use in query to extract name from category table but couldn't figure it out.
You should fix your data model. But, you can do this in SQL Server:
select a.*, s.names
from article a cross apply
(select string_agg(c.name, ',') as names
from string_split(a.categories, '|') ss join
category c
on try_convert(int, ss.value) = c.id
) s;
Here is a db<>fiddle.
Presumably, you already know the shortcomings of this data model:
SQL Server has poor string handling functionality.
Numbers should be stored as numbers not strings.
Foreign key references should be properly declared.
Such queries cannot make use of indexes and partitions.
If you really want to store multiple values in a field, SQL Server offers both JSON and XML. Strings are not the right approach.

For MS Access SQL, want to use EXCEPT in Access for three columns in table1 to 1 column in table 2

I have 3 columns in table A. I am trying to design a query that will call out all the values (in the three columns) that do not apepar in the 1 column I have in table B. If it helps to make it more clear, table B is a list of currencies in ISO codes and table A is three columns of currencies being used, I am identifying all those values that are NOT using ISO codes to denote their currency.
Currently, I can't seem to get them all to match to the one column, so I made 2 more columns in table B so I can match them individually. My constraints are, I cannot change table A and I must do this in one query. What I got so far is below.
SELECT m.Currency1, i.ISO_Code, m.Currency2 , i.ISO_Code1, m.Currency3, i.ISO_Code2
FROM A AS m
LEFT JOIN B AS i
ON m.Currency=i.ISO_Code
AND m.Currency2=i.ISO_Code1
AND m.Currency3=i.ISO_Code2
WHERE i.ISO_Code is NULL
OR i.ISO_Code1 is NULL
OR i.ISO_Code2 is NULL;
I wouldn't bother making multiple columns in 'B'. I played with this in SQLFiddle and got it to work.
Something like this:
SELECT
m.Currency1, i.ISO_Code,
m.Currency2, j.ISO_Code AS ISO_Code1,
m.Currency3, k.ISO_Code AS ISO_Code2
FROM A AS m
LEFT JOIN B as i
ON m.Currency1 = i.ISO_Code
LEFT JOIN B as j
ON m.Currency2 = j.ISO_Code
LEFT JOIN B as k
ON m.Currency3 = k.ISO_Code
WHERE
i.ISO_Code IS NULL OR
j.ISO_Code IS NULL OR
k.ISO_Code IS NULL

Large inner join

We have 2 tables with English words: words_1 and words_2 with fields(word as VARCHAR, ref as INT), where word - it's an english word, ref - reference on another(third) table(it's not important).
In each table all words are unique. First table contains some words that are not in second one(and on the contrary second table contains some unique words).
But most words in two tables are same.
Need to get: Result table with all distinct words and ref's.
Initial conditions
Ref's for same tables can be different( dictionaries were loaded from different places).
Words count 300 000 in each table, so inner join is not convinient
Examples
words_1
________
Health-1
Car-3
Speed-5
words_2
_________
Health-2
Buty-6
Fast-8
Speed-9
Result table
_____________
Health-1
Car-3
Speed-5
Buty-6
Fast-8
select word,min(ref)
from (
select word,ref
from words_1
union all
select word,ref
from words_2
) t
group by word
Try using a full outer join:
select coalesce(w1.word, w2.word) as word, coalesce(w1.ref, w2.ref) as ref
from words_1 w1 full outer join
words_2 w2
on w1.word = w2.word;
The only time this will not work is if ref can be NULL in either table. In that case, change the on to:
on w1.word = w2.word and w1.ref is not null and w2.ref is not null
If you want to improve performance, just create an index on the tables:
create index idx_words1_word_ref on words_1(word, ref);
create index idx_words2_word_ref on words_2(word, ref);
A join is quite doable and even without the index, SQL Server should be smart enough to come up with a reasonable implementation.

How to build virtual columns?

Sorry if this is a basic question. I'm fairly new to SQL, so I guess I'm just missing the name of the concept to search for.
Quick overview.
First table (items):
ID | name
-------------
1 | abc
2 | def
3 | ghi
4 | jkl
Second table (pairs):
ID | FirstMember | SecondMember Virtual column (pair name)
-------------------------------------
1 | 2 | 3 defghi
2 | 1 | 4 abcjkl
I'm trying to build the virtual column shown in the second table
It could be built at the time any entry is made in the second table, but if done that way, the data in that column would get wrong any time one of the items in the first table is renamed.
I also understand that I can build that column any time I need it (in either plain requests or stored procedures), but that would lead to code duplication, since the second table can be involved in multiple different requests.
So is there a way to define a "virtual" column, that could be accessed as a normal column, but whose content is built dynamically?
Thanks.
Edit: this is on MsSql 2008, but an engine-agnostic solution would be preferred.
Edit: the example above was oversimplified in multiple ways - the major one being that the virtual column content isn't a straight concatenation of both names, but something more complex, depending on the content of columns I didn't described. Still, you've provided multiple paths that seems promising - I'll be back. Thanks.
You need to join the items table twice:
select p.id,
p.firstMember,
p.secondMember,
i1.name||i2.name as pair_name
from pairs as p
join items as i1 on p.FirstMember = i1.id
join items as i2 on p.SecondMember = i2.id;
Then put this into a view and you have your "virtual column". You would simply query the view instead of the actual pairs table wherever you need the pair_name column.
Note that the above uses inner joins, if your "FirstMember" and "SecondMember" columns might be null, you probably want to use an outer join instead.
You can use a view, which creates a table-like object from a query result, such as the one with a_horse_with_no_name provided.
CREATE VIEW pair_names AS
SELECT p.id,
p.firstMember,
p.secondMember,
CONCAT(i1.name, i2.name) AS pair_name
FROM pairs AS p
JOIN items AS i1 ON p.FirstMember = i1.id
JOIN items AS i2 ON p.SecondMember = i2.id;
Then to query the results just do:
SELECT id, pair_name FROM pair_names;
You could create a view for your 'virtual column', if you wanted to, like so:
CREATE VIEW aView AS
SELECT
p.ID,
p.FirstMember,
p.SecondMember,
a.name + b.name as 'PairName'
FROM
pairs p
LEFT JOIN
items a
ON
p.FirstMember = a.ID
LEFT JOIN
items b
ON
p.SecondMember = b.ID
Edit:
Or, of course, you could just use a similar select statement every time.
When selecting from tables you can name the results of a column using AS.
SELECT st.ID, st.FirstMember, st.SecondMember, ft1.Name + ft2.Name AS PairName
FROM Second_Table st
JOIN First_Table ft1 ON st.FirstMember = ft1.ID
JOIN First_Table ft2 ON st.SecondMember = ft2.ID
Should give you something like what you are after.

SQL left joining tables with ambiguous column names

I need to left join a few tables in a query where the column names are ambiguous.
ListingCategory_Listings:
ID | ListingCategoryID | ListingID | ..
SiteTree_Live:
ID | ClassName | Title | Content | ..
ListingCategory:
ID | IconID
File:
ID | ClassName | Name | Title | Filename | ..
I have the query:
SELECT * FROM ListingCategory_Listings
LEFT JOIN Listing ON ListingCategory_Listings.ListingID = Listing.ID
LEFT JOIN SiteTree_Live ON Listing.ID = SiteTree_Live.ID
LEFT JOIN ListingCategory ON ListingCategory_Listings.ListingCategoryID = ListingCategory.ID
LEFT JOIN File ON ListingCategory.IconID = File.ID
Both the listing and listing category data is stored in the SiteTree_Live table, when my records are returned, obviously, the Title, ID, Content and other ambiguous fields are returned under the heading Title, ID, Content.
I need to access both the Listing Title and The Listing Category Title and other specific information. If they had their data stored in different tables I could use select Listing.Title AS lTitle but how can I do something similar in this situation?
You need to alias the columns and give them different labels.
You should always put only the fields you need into the SELECT clause, listing each individually rather than using *.
Good practice also dictates giving each table a short alias.
SELECT L.Title Listing_Title, LC.Title ListingCategory_Title,
... all your other columns ...
FROM ListingCategory_Listings LCL
LEFT JOIN Listing L ON LCL.ListingID = L.ID
LEFT JOIN SiteTree_Live STL ON L.ID = STL.ID
LEFT JOIN ListingCategory LC ON LCL.ListingCategoryID = LC.ID
LEFT JOIN File F ON LC.IconID = F.ID
I suggest that to ALIAS a COLUMN name, the previous information may not be correct.
The proper syntax for COLUMN ALIAS is :
SELECT column_name AS alias_name
FROM table_name;
In a COLUMN ALIAS, the COLUMN alias assignment is declared beside the proper COLUMN name.
The proper syntax for TABLE ALIAS is :
SELECT column_name(s)
FROM table_name AS alias_name;
In a TABLE ALIAS, the TABLE alias is put beside the column being selected in the SELECT portion of the statement. The TABLE ALAIS assignment is made in the FROM portion of the statement.
The difference being that the alias assignment is made beside the proper name of what is desired to be ALIASED!
I set up a practice on the Fraser's provided example & it worked for me. I believe the answer provided was speaking of TABLE ALIAS, not COLUMN ALIAS.
I disagree with ALIAS usage where the TABLE or COLUMN ALIAS is so short that it complicates the readability of the code. I believe it should be short, but not so short that the code is not 'self documenting' for maintenance purposes. That is my 2ยข of thought on it!