SQL - Joining columns from the same table in a Query - sql
SOLVED ! See the answer bellow !
Before I explain my problem I want to apologise for those who would feel this question is too long but I feel like I must give some details to make things the clearer possible. Though, the problem is simple to understand it is not that simple to me to implement.
I have 3 tables.
Hata and Icon contains images I want to link with Succes which contains texts
[Hata]
id, INTEGER, AUTO_INCREMENT, PRIMARY_KEY
hata Image
idLang, VARCHAR(5)
[Icon]
id, INTEGER, AUTO_INCREMENT, PRIMARY_KEY
icon, IMAGE
idPhrase, INTEGER
[Succes]
id, INTEGER, AUTO_INCREMENT, PRIMARY_KEY
idPhrase, INTEGER
titre, VARCHAR(25)
desc, VARCHAR(125)
idLang, VARCHAR(5)
Here is a sample showing how the Succes table looks like
+----+----------+-----------------+------------------+--------+
| id | idPhrase | titre | desc | idLang |
+----+----------+-----------------+------------------+--------+
| 1 | 1 | Hello | Desc in English | en-GB |
+----+----------+-----------------+------------------+--------+
| 2 | 1 | Salut | Desc in French | fr-FR |
+----+----------+-----------------+------------------+--------+
| 3 | 1 | 今日は | Desc in Japanese | ja-JP |
+----+----------+-----------------+------------------+--------+
| 4 | 2 | Goodbye | Desc in English | en-GB |
+----+----------+-----------------+------------------+--------+
| 5 | 2 | Au revoir | Desc in French | fr-FR |
+----+----------+-----------------+------------------+--------+
| 6 | 2 | またね | Desc in Japanese | ja-JP |
+----+----------+-----------------+------------------+--------+
| 7 | 3 | You're welcome | Desc in English | en-GB |
+----+----------+-----------------+------------------+--------+
| 8 | 3 | Je vous en prie | Desc in French | fr-FR |
+----+----------+-----------------+------------------+--------+
| 9 | 3 | どういたしまして | Desc in Japanese | ja-JP |
+----+----------+-----------------+------------------+--------+
...
The tables are now joined using this WHERE conditions
Icons.idPhrase = Succes.idPhrase AND Hata.idLang=Succes.idLang
Everything would be fine if there would be nothing specific in the Succes table.
In fact, for each Icon there are 3 sentences and the idPhrase links them but in the actual result set I somehow have redundancies.
Icon1|FlagIcon1|TitreLang1|DescLang1
Icon1|FlagIcon2|TitreLang2|DescLang2
Icon1|FlagIcon3|TitreLang3|DescLang3
Icon2|FlagIcon1|TitreLang1|DescLang1
Icon2|FlagIcon2|TitreLang2|DescLang2
Icon2|FlagIcon3|TitreLang3|DescLang3
...
What I'd like to achieve is the following (just the very first row):
Icon1|FlagIcon1|TitreLang1|DescLang1|FlagIcon2|TitreLang2|DescLang2|FlagIcon3|TitreLang3|DescLang
or
Icon1|FlagIcon1|FlagIcon2|FlagIcon3|TitreLang1|DescLang1|TitreLang2|DescLang2|TitreLang3|DescLang3
or even
Icon1|FlagIcon1|FlagIcon2|FlagIcon3|TitreLang1|TitreLang2|TitreLang3|DescLang1|DescLang2|DescLang3
In other words, it would be like I'd joined several queries together such as
SELECT icon FROM Icon
Joined with
SELECT Hata.hata AS fEN, Succes.titre AS tEN, Succes.desc AS dEN
FROM Hata, Succes
WHERE Hata.idLang=Succes.idLang AND Succes.idLang='en-GB'
Joined With
SELECT Hata.hata AS fFR, Succes.titre AS tFR, Succes.desc AS dFR
FROM Hata, Succes
WHERE Hata.idLang=Succes.idLang AND Succes.idLang='fr-FR'
And so on...
Just the problem of ensuring the links between tables (icon 1 with sentence 1)
Here's another sample on how it should (may) look like
+-------+-------+-------+-------+----------------+------------------+------------+-----------------+----------------+------------------+
| icon | fEN | fFR | fJP | tEN | tFR | tJA | dEN | dFR | dJA |
+-------+-------+-------+-------+----------------+------------------+------------+-----------------+----------------+------------------+
| <img> | <img> | <img> | <img> | Hello | Salut | 今日は | Desc in English | Desc in French | Desc in Japanese |
+-------+-------+-------+-------+----------------+------------------+------------+-----------------+----------------+------------------+
| <img> | <img> | <img> | <img> | Goodbye | Au revoir | またね | Desc in English | Desc in French | Desc in Japanese |
+-------+-------+-------+-------+----------------+------------------+------------+-----------------+----------------+------------------+
| <img> | <img> | <img> | <img> | You're welcome | Je vous en pries | どういたしまして | Desc in English | Desc in French | Desc in Japanese |
+-------+-------+-------+-------+----------------+------------------+------------+-----------------+----------------+------------------+
...
I've browsed for SQL reference to try many things but they don't seem to do what I expect (CONCATENATE, UNION, etc...)
I also tried the following query but it gives me an error message.
SELECT Icon.icon, Hata.hata AS fEN,Hata.hata AS fFR,Hata.hata AS fJA
,'FR'.'titre', 'FR'.'desc'
,'JA'.'titre', 'JA'.'desc'
,'UK'.'titre', 'UK'.'desc'
FROM Hata, Icon
LEFT JOIN Succes AS FR ON 'FR'.'idLang' = 'Hata'.'idLang' AND 'FR'.'idLang' = 'fr-FR'
LEFT JOIN Succes AS JA ON 'JA'.'idLang' = 'Hata'.'idLang' AND 'FR'.'idLang' = 'ja-JP'
LEFT JOIN Succes AS UK ON 'UK'.'idLang' = 'Hata'.'idLang' AND 'FR'.'idLang' = 'en-GB'
the message is
Statut SQL: HY000
Error Code: 1000
syntax error, unexpected $end, expecting BETWEEN or IN or SQL_TOKEN_LIKE
but it seems my syntax is good according to sample I've found even on StackOverflow.
I must also specify that I'm using OpenOffice Base and my purpose is publishing a document. Maybe there is something specific to OOo such as LEFT JOIN not implemented but the code get coloured so I think it should be fine.
Thank you for your availability and help.
I really don't get it.
I've tried with MySQL and it does something like an exclusive join
mysql> SELECT titre AS tfr, titre AS ten, titre AS tjp FROM data WHERE idlang=1
-> UNION
-> SELECT null,titre AS ten, null FROM data WHERE idlang=2
-> UNION
-> SELECT null, null, titre as tjp FROM data WHERE idlang=3;
+------------------+------------------+------------------+
| tfr | ten | tjp |
+------------------+------------------+------------------+
| Salut | Salut | Salut |
| Au revoir | Au revoir | Au revoir |
| Je vous en pries | Je vous en pries | Je vous en pries |
| NULL | Hello | NULL |
| NULL | Goodbye | NULL |
| NULL | You're Welcome | NULL |
| NULL | NULL | Konnichiha |
| NULL | NULL | Mata ne |
| NULL | NULL | Douitashimashite |
+------------------+------------------+------------------+
9 rows in set (0.00 sec)
If in the 1st SELECT I do titre AS tfr, null, null the column headers get to null.
mysql> SELECT titre AS tfr, titre AS ten, titre AS tjp FROM data WHERE idlang=1
-> UNION
-> SELECT null,titre AS ten, null FROM data WHERE idlang=2
-> UNION
-> SELECT null, null, titre as tjp FROM data WHERE idlang=3;
+------------------+------------------+------------------+
| tfr | NULL | NULL |
+------------------+------------------+------------------+
| Salut | NULL | NULL |
| Au revoir | NULL | NULL |
| Je vous en pries | NULL | NULL |
| NULL | Hello | NULL |
| NULL | Goodbye | NULL |
| NULL | You're Welcome | NULL |
| NULL | NULL | Konnichiha |
| NULL | NULL | Mata ne |
| NULL | NULL | Douitashimashite |
+------------------+------------------+------------------+
It still doesn't looks like the result I want.
I need to concentrate on that data table to get all in one line but I keep wondering how to achieve this. In principle it is very simple but I can't translate that in SQL.
mysql> DESCRIBE data;
+-----------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| id_phrase | int(11) | YES | | NULL | |
| titre | varchar(20) | YES | | NULL | |
| desc | varchar(50) | YES | | NULL | |
| idicon | int(11) | YES | | NULL | |
| idlang | int(11) | YES | | NULL | |
+-----------+-------------+------+-----+---------+----------------+
6 rows in set (0.00 sec)
Actually idicon is redundant with id_phrase (don't really need it so pretend it does not exist).
Thank you.
HERE ARE SOME PROPOSITIONS IF YOU WOULD MEET A RESEMBLING PROBLEM.
NB: The column names and table names may differ from the original question but the problem is the same.
I've been asking this question on another forum and here are 2 queries I tested and can certify are working with MySQL 5.5
Query 1 :
SELECT id_phrase
, idicon
, max(case idlang when 1 then titre end) AS tfr
, max(case idlang when 1 then DESC end) AS dfr
, max(case idlang when 2 then titre end) AS ten
, max(case idlang when 2 then DESC end) AS den
, max(case idlang when 3 then titre end) AS tjp
, max(case idlang when 3 then DESC end) AS djp
FROM DATA
WHERE idlang IN (1, 2, 3)
GROUP BY id_phrase, idicon
ORDER BY id_phrase ASC
Query 2 :
SELECT t1.id_phrase, t1.idicon, t1.titre AS tfr, t1.descr AS dfr, t2.titre AS ten, t2.descr AS den, t3.titre AS tjp, t3.descr AS djp
FROM DATA AS t1
LEFT OUTER JOIN DATA AS t2
ON t1.id_phrase=t2.id_phrase
LEFT OUTER JOIN DATA AS t3
ON t1.id_phrase=t3.id_phrase
WHERE t1.idlang=1 AND t2.idlang=2 AND t3.idlang=3
You're welcome if these queries may help you.
Source (french)
Related
Create SQL SELECT Statement to 'modify' small table
I am pretty new to SQL in general and want to 'modify' the following table (Tablename: translations / PK: id, language, key): | id | language | key | value | --------------------------------------------- | 1 | DE | #Keyboard | Tastatur | | 1 | EN | #Keyboard | Keyboard | | 1 | ES | #Keyboard | Teclado | | 2 | DE | #Screen | Bildschirm | | 2 | EN | #Screen | Screen | | 3 | ES | #Equipment | Equipo | I want to get a table like this: | key | valueDE | valueEN | -------------------------------------------- | #Keyboard | Tastatur | Keyboard | | #Screen | Bildschirm | Screen | | #Equipment | null | null | So basically I want the translation of the values for the languages DE and EN next to each other in columns. Entries with no translation for DE and EN should have an empty field in the column valueDE and valueEN or if only one translation is available the other field in the column should be empty as shown in the table above. Help would be appreciated a lot.
Here it is, your definition re-worded into SQL. select key, (select value from translations where language='DE' and key=t.key limit 1) "valueDE", (select value from translations where language='EN' and key=t.key limit 1) "valueEN" from (select distinct key from translations) t; key valueDE valueEN #Keyboard Tastatur Keyboard #Equipment #Screen Bildschirm Screen
When Querying Many-To-Many Relationship in SQL, Return Multiple Connections As an Array In Single Row?
Basically, I have 3 tables, titles, providers, and provider_titles. Let's say they look like this: | title_id | title_name | |------------|----------------| | 1 | San Andres | | 2 |Human Centipede | | 3 | Zoolander 2 | | 4 | Hot Pursuit | | provider_id| provider_name | |------------|----------------| | 1 | Hulu | | 2 | Netflix | | 3 | Amazon_Prime | | 4 | HBO_GO | | provider_id| title_id | |------------|----------------| | 1 | 1 | | 1 | 2 | | 2 | 1 | | 3 | 1 | | 3 | 3 | | 4 | 4 | So, clearly there are titles with multiple providers, yeah? Typical many-to-many so far. So what I'm doing to query it is with a JOIN like the following: SELECT * FROM provider_title JOIN provider ON provider_title.provider_id = provider.provider_id JOIN title ON title.title_id = provider_title.title_id WHERE provider.name IN ('Netflix', 'HBO_GO', 'Hulu', 'Amazon_Prime') Ok, now to the actual issue. I don't want repeated title names back, but I do want all of the providers associated with the title. Let me explain with another table. Here is what I am getting back with the current query, as is: | provider_id| provider_name | title_id | title_name | |------------|---------------|----------|---------------| | 1 | Hulu | 1|San Andreas | | 1 | Hulu | 2|Human Centipede| | 2 | Netflix | 1|San Andreas | | 3 | Amazon_Prime | 1|San Andreas | | 3 | Amazon_prime | 3|Zoolander 2 | | 4 | HBO_GO | 4|Hot Pursuit | But what I really want would be something more like | provider_id| provider_name |title_id| title_name| |------------|-----------------------------|--------|-----------| | [1, 2, 3] |[Hulu, Netflix, Amazon_Prime]| 1|San Andreas| Meaning I only want distinct titles back, but I still want each title's associated providers. Is this only possible to do post-sql query with logic iterating through the returned rows?
Depending on your database engine, there may be an aggregation function to help achieve this. For example, this SQLfiddle demonstrates the postgres array_agg function: SELECT t.title_id, t.title_name, array_agg( p.provider_id ), array_agg( p.provider_name ) FROM provider_title as pt JOIN provider as p ON pt.provider_id = p.provider_id JOIN title as t ON t.title_id = pt.title_id GROUP BY t.title_id, t.title_name Other database engines have equivalents. For example: mySQL has group_concat Oracle has listagg sqlite has group_concat (as well!) If your database isn't covered by the above, you can google '[Your database engine] aggregate comma delimited string'
Fetch SQL rows with priority
I have following tables structure: forms RID | MODULE ------------ 1 | indiv 2 | indiv 3 | indiv translations RID | LANG | VALUE | MODULE | TAG | ----------------------------------- 1 | en |car | | | 1 | en |truck |indiv | | 1 | en |boat |indiv |C100 | 2 | en |hat | | | 3 | en |cat | | | 3 | en |dog |indiv | | 4 | en |light | | | 5 | en |dark | | | I need to fetch only one row per RID from translations table, based on additional (but not mandatory) parameters for module and tag columns, i.e.: RESULT without input parameters: RID | LANG | VALUE | MODULE | TAG | ----------------------------------- 1 | en |car | | | 2 | en |hat | | | 3 | en |cat | | | RESULT with one input parameter module='indiv': RID | LANG | VALUE | MODULE | TAG | ----------------------------------- 1 | en |truck |indiv | | 2 | en |hat | | | 3 | en |dog |indiv | | If I have two input parameters the result to be: RESULT with two parameters: module='indiv' AND tag='c100' RID | LANG | VALUE | MODULE | TAG | ----------------------------------- 1 | en |boat |indiv |C100 | 2 | en |hat | | | 3 | en |dog |indiv | | How can I achieve this with SQL only on ORACLE DB server? A query example for the last case with two parameters will be enough for me as previous cases are subsets from last one with NULL of these columns I believe. If you think that all these cases are too different and require different SQL statements, you are more than welcome to write them as well. Thank you!
SELECT * FROM ( SELECT t.*, ROW_NUMBER() OVER ( PARTITION BY RID ORDER BY CASE WHEN module = LOWER( :mod ) AND tag = UPPER( :tag ) THEN 1 WHEN tag = UPPER( :tag ) THEN 2 WHEN module = LOWER( :mod ) AND tag IS NULL THEN 3 WHEN module IS NULL AND tag IS NULL THEN 4 ELSE 5 END ) AS rn FROM translations t WHERE ( module IS NULL OR module = LOWER( :mod ) ) OR ( tag IS NULL OR tag = UPPER( :tag ) ) ) WHERE rn = 1;
I think this should do. SELECT * FROM ( SELECT F.RID, T.LANG, T.VALUE, T.MODULE, T.TAG, RANK() OVER(PARTITION BY F.RID ORDER BY DECODE(T.MODULE, :module, 1, 2), DECODE(T.TAG, :tag, 1, 2)) RANK FROM forms F INNER JOIN translations T ON T.RID = F.RID) WHERE RANK = 1 So you rank rows with MODULE = :module or/and TAG = :tag higher. You still need something to do with ties, but you get the idea. RANK leaves ties, ROW_NUMBER does not. I put MODULE higher than TAG because of your examples. You might need to change it if you can input tags without modules. And also, DECODE maps NULL to NULL, so if :module is not set, you will get match with rows having NULL in MODULE.
Retrieve data from SQL Server and concatenate results over rows based on grouping
I’ve been working on a problem for a couple of days and have finally managed to work out a solution that works for me. In case this solution is useful for someone else, I’m going to ask a question and answer it myself. I have read-only access to a large SQL Server database containing in excess of 1 million records. Some of the tables in the database are linked in many-to-many relationships through lookup tables. To simplify matters, the tables can be illustrated as shown below: table names |-----------| | id | name | |----|------| | 1 | dave | | 2 | phil | | 3 | john | table foods_relationship table clothes_relationship | 4 | pete | |--------------------------| |----------------------------| |-----------| | id | names_id | foods_id | | id | names_id | clothes_id | |----|----------|----------| |----|----------|------------| table foods | 1 | 1 | 1 | | 1 | 1 | 1 | |---------------| | 2 | 1 | 3 | | 2 | 1 | 3 | | id | food | | 3 | 1 | 4 | | 3 | 1 | 4 | |----|----------| | 4 | 2 | 2 | | 4 | 2 | 2 | | 1 | beef | | 5 | 2 | 3 | | 5 | 2 | 3 | | 2 | tomatoes | | 6 | 2 | 4 | | 6 | 2 | 4 | | 3 | bacon | | 7 | 2 | 5 | | 7 | 3 | 1 | | 4 | cheese | | 8 | 3 | 3 | | 8 | 3 | 3 | | 5 | apples | | 9 | 3 | 5 | | 9 | 3 | 5 | |---------------| | 10 | 4 | 1 | | 10 | 4 | 2 | | 11 | 4 | 2 | | 11 | 4 | 4 | table clothes | 12 | 4 | 3 | | 12 | 4 | 5 | |---------------| | 13 | 4 | 5 | |----------------------------| | id | clothes | |--------------------------| |----|----------| | 1 | trousers | | 2 | shorts | | 3 | shirt | | 4 | socks | | 5 | jumper | | 6 | jacket | |---------------| The tables can be recreated using the following SQL (adapted from MySQL database so may need minor tweaking to work in SQL Server): CREATE TABLE `clothes` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `clothes` varchar(32) DEFAULT NULL, PRIMARY KEY (`id`) ); INSERT INTO `clothes` (`id`, `clothes`) VALUES (1,'trousers'), (2,'shorts'), (3,'shirt'), (4,'socks'), (5,'jumper'), (6,'jacket'); CREATE TABLE `clothes_relationships` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `names_id` int(11) DEFAULT NULL, `clothes_id` int(11) DEFAULT NULL, PRIMARY KEY (`id`) ); INSERT INTO `clothes_relationships` (`id`, `names_id`, `clothes_id`) VALUES (1,1,1), (2,1,3), (3,1,4), (4,2,2), (5,2,3), (6,2,4), (7,3,1), (8,3,3), (9,3,5), (10,4,2), (11,4,4), (12,4,5); CREATE TABLE `food_relationships` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `names_id` int(11) DEFAULT NULL, `foods_id` int(11) DEFAULT NULL, PRIMARY KEY (`id`) ); INSERT INTO `food_relationships` (`id`, `names_id`, `foods_id`) VALUES (1,1,1), (2,1,3), (3,1,4), (4,2,2), (5,2,3), (6,2,4), (7,2,5), (8,3,3), (9,3,5), (10,4,1), (11,4,2), (12,4,3), (13,4,5); CREATE TABLE `foods` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `food` varchar(32) DEFAULT NULL, PRIMARY KEY (`id`) ); INSERT INTO `foods` (`id`, `food`) VALUES (1,'beef'), (2,'tomatoes'), (3,'bacon'), (4,'cheese'), (5,'apples'); CREATE TABLE `names` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(32) DEFAULT NULL, PRIMARY KEY (`id`) ); INSERT INTO `names` (`id`, `name`) VALUES (1,'dave'), (2,'phil'), (3,'john'), (4,'pete'); I want to query the database and – somehow – get the following output: |-------------------------------------------------------------| | name | food | clothes | |------|------------------------------|-----------------------| | dave | beef,cheese,bacon | trousers,socks,shirt | | john | apples,bacon | jumper,shirt,trousers | | pete | beef,apples,bacon,tomatoes | shorts,jumper,socks | | phil | bacon,tomatoes,apples,cheese | shirt,shorts,socks | |-------------------------------------------------------------| However, running a SELECT query that joins the ‘names’ table to one or both of the other tables (via the respective lookup tables) results in multiple rows for each name. For example: SELECT names.name, foods.food FROM names LEFT JOIN food_relationships ON names.id = food_relationships.names_id LEFT JOIN foods ON food_relationships.foods_id = foods.id; ...produces the following set of results: |-----------------| | name | food | |------|----------| | dave | beef | | dave | bacon | | dave | cheese | | phil | tomatoes | | phil | bacon | | phil | cheese | | phil | apples | | john | bacon | | john | apples | | pete | beef | | pete | tomatoes | | pete | bacon | | pete | apples | |-----------------| The problem is compounded even further if the SELECT query returns data from both tables: SELECT names.name, foods.food, clothes.clothes FROM names LEFT JOIN food_relationships ON names.id = food_relationships.names_id LEFT JOIN foods ON food_relationships.foods_id = foods.id LEFT JOIN clothes_relationships ON names.id = clothes_relationships.names_id LEFT JOIN clothes ON clothes_relationships.clothes_id = clothes.id; |-----------------------------| | name | food | clothes | |------|----------|-----------| | dave | beef | trousers | | dave | beef | shirt | | dave | beef | socks | | dave | bacon | trousers | | dave | bacon | shirt | | dave | bacon | socks | | dave | cheese | trousers | | dave | cheese | shirt | | dave | cheese | socks | | phil | tomatoes | shorts | | phil | tomatoes | shirt | | phil | tomatoes | socks | | phil | bacon | shorts | | phil | bacon | shirt | | phil | bacon | socks | | phil | cheese | shorts | | phil | cheese | shirt | | phil | cheese | socks | | phil | apples | shorts | | phil | apples | shirt | | phil | apples | socks | | ... | etc. The question is, how can I query the SQL Server database to retrieve all the data but process it to have only one line per person?
If the database were MySQL, the solution would be relatively easy because MySQL has a GROUP_CONCAT function which concatenates rows. So, for just one of the tables, I could use: SELECT names.name, GROUP_CONCAT(foods.food) FROM names LEFT JOIN food_relationships ON names.id = food_relationships.names_id LEFT JOIN foods ON food_relationships.foods_id = foods.id GROUP BY (names.name); ...to give: name food dave beef,cheese,bacon john apples,bacon pete beef,apples,bacon,tomatoes phil bacon,tomatoes,apples,cheese To get equivalent data from both ‘names’ and ‘clothes’ tables, I could use something like: SELECT temp_foods_table.name AS 'name', temp_foods_table.food AS 'food', temp_clothes_table.clothes AS 'clothes' FROM ( SELECT names.name, GROUP_CONCAT(foods.food) AS 'food' FROM names LEFT JOIN food_relationships ON names.id = food_relationships.names_id LEFT JOIN foods ON food_relationships.foods_id = foods.id GROUP BY (names.name) ) AS temp_foods_table LEFT JOIN ( SELECT names.name, GROUP_CONCAT(clothes.clothes) AS 'clothes' FROM names LEFT JOIN clothes_relationships ON names.id = clothes_relationships.names_id LEFT JOIN clothes ON clothes_relationships.clothes_id = clothes.id GROUP BY (names.name) ) AS temp_clothes_table ON temp_foods_table.name = temp_clothes_table.name; ...to give the following results: name food clothes dave beef,cheese,bacon trousers,socks,shirt john apples,bacon jumper,shirt,trousers pete beef,apples,bacon,tomatoes shorts,jumper,socks phil bacon,tomatoes,apples,cheese shirt,shorts,socks However, the situation in SQL SERVER appears to be much less straight-forward. For a single table there are some proposed solutions online which include the use of common table expressions or FOR XML PATH. However, all the solutions appear to have drawbacks and give the distinct impression that they are work-arounds rather than specifically-designed features. Each suggested solution has some weakness (for example, the FOR XML PATH solution assumes that the text is XML and, therefore, special characters included in the text can cause problems). In addition, some commenters expressed concerns that such work-arounds were based on undocumented or deprecated features and, as a result, may not be reliable in the long-term. As a result, I decided not to tie myself up in SQL knots but to process the data post-retrieval using Python and Pandas. I always transfer data to a Pandas dataframe for plotting and analysis anyway so this wasn't a major inconvenience. In order to concatenate the data over several columns, I used groupby(). However, since there were two many-to-many tables, there was duplication in each column and, therefore, the final concatenated string contained all those duplications. In order to have only unique values, I used Python sets (which, by definition, can only contain unique values). The only potential drawback with this method is that the order of the strings is not maintained but, for my situation, that isn't an issue. The final Python solution looked something like this: Import necessary libraries: >>> import pandas as pd >>> import pymssql >>> import getpass Input necessary details to connect to database: >>> myServer = input("Enter server address: ") >>> myUser = input("Enter username: ") >>> myPwd = getpass.getpass("Enter password: ") Create a connection: >>> myConnection = pymssql.connect(server=myServer, user=myUser, password=myPwd, port='1433') Define a query to retrieve the necessary data: >>> myQuery = """SELECT names.name, foods.food, clothes.clothes FROM names LEFT JOIN food_relationships ON names.id = food_relationships.names_id LEFT JOIN foods ON food_relationships.foods_id = foods.id LEFT JOIN clothes_relationships ON names.id = clothes_relationships.names_id LEFT JOIN clothes ON clothes_relationships.clothes_id = clothes.id """ Run query, put results in dataframe and close connection: >>> myLatestData = pd.io.sql.read_sql(myQuery, con=myConnection) >>> myConnection.close() Concatenate strings in multiple rows and remove duplicates: >>> tempDF = tempDF.groupby('name').agg(lambda col: ','.join(set(col))) Print final dataframe: >>> print(tempDF) name food clothes dave beef,bacon,cheese socks,trousers,shirt john bacon,apples jumper,trousers,shirt pete tomatoes,beef,bacon,apples socks,jumper,shorts phil tomatoes,bacon,cheese,apples socks,shorts,shirt For me, this solution makes much more intuitive sense than trying to do all the data processing within the SQL query. Hope this helps someone else.
If it is MS-Sql Server.. You can use STUFF function. For e.g. DECLARE #Heroes TABLE ( [HeroName] VARCHAR(20) ) INSERT INTO #Heroes ( [HeroName] ) VALUES ( 'Superman' ), ( 'Batman' ), ('Ironman'), ('Wolverine') SELECT STUFF((SELECT ',' + [HeroName] FROM #Heroes ORDER BY [HeroName] FOR XML PATH('')), 1, 1, '') AS [Output] Output Batman,Ironman,Superman,Wolverine I think this should answer your question. Thanks
Joining two tables and show data from one if there is any
I have these two tables that i need to join fields_data fields +------------+-----------+------+ +------+-------------+----------+ | relationid | fieldname | data | | name | displayname | position | +------------+-----------+------+ +------+-------------+----------+ | 2 | ftp | test | | user | Username | top | | 2 | other | 1234 | | pass | Password | top | +------------+-----------+------+ | ftp | FTP | top | | log | Log | top | | txt | Text | mid | +------+-------------+----------+ I want to get all the rows from the "fields" table if they have the position "top" AND if a row has a match on name = fieldname from fields_data it should also show the data. This is my join SELECT fd.`data`, fd.`relationid`, fd.`fieldname`, f.`name`, f.`displayname` FROM `fields` AS f LEFT OUTER JOIN `fields_data` AS fd ON fd.`fieldname` = f.`name` WHERE f.`position`='top' AND (fd.`relationid`='3' OR fd.`relationid` IS NULL) My problem is that the above query only gives me this result: +------+------------+-----------+------+-------------+ | data | relationid | fieldname | name | displayname | +------+------------+-----------+------+-------------+ | NULL | NULL | NULL | user | Username | | NULL | NULL | NULL | pass | Password | | NULL | NULL | NULL | log | Log | +------+------------+-----------+------+-------------+ The field called "ftp" is missing due to it having a relation to "2".. However i still want to display it as result but like the others with NULL in it. And if the SQL query had "fd.relationid='2'" instead of 3 it would give same result, but with the row containing ftp in name, holding data in the three fields. I hope you get what i mean.. My english is not the best.. Heres the result i want: with above query containing fd.`relationid`='3' +------+------------+-----------+------+-------------+ | data | relationid | fieldname | name | displayname | +------+------------+-----------+------+-------------+ | NULL | NULL | NULL | user | Username | | NULL | NULL | NULL | pass | Password | | NULL | NULL | NULL | ftp | FTP | | NULL | NULL | NULL | log | Log | +------+------------+-----------+------+-------------+ with above query containing fd.`relationid`='2' +------+------------+-----------+------+-------------+ | data | relationid | fieldname | name | displayname | +------+------------+-----------+------+-------------+ | NULL | NULL | NULL | user | Username | | NULL | NULL | NULL | pass | Password | | test | 2 | ftp | ftp | FTP | | NULL | NULL | NULL | log | Log | +------+------------+-----------+------+-------------+
You want to move the condition to the on clause: SELECT fd.`data`, fd.`relationid`, fd.`fieldname`, f.`name`, f.`displayname` FROM `fields` f LEFT OUTER JOIN `fields_data` fd ON fd.`fieldname` = f.`name` AND fd.`relationid` = '3' WHERE f.`position`='top' ; It is interesting that the semantics of your query and this query are different -- and you found the exact situation: when there is a match on another value, the where clause form filters out the row. This will still keep everything. As a note, the following also does what you want: SELECT fd.`data`, fd.`relationid`, fd.`fieldname`, f.`name`, f.`displayname` FROM `fields` f LEFT OUTER JOIN (SELECT fd.* FROM `fields_data` fd WHERE fd.`relationid` = '3' ) fd ON fd.`fieldname` = f.`name` WHERE f.`position` = 'top' ; I wouldn't recommend writing the query this way, particularly in MySQL (because the subquery is materialized). However, understanding why your version is different from these versions (and why these are the same) is a big step forward in mastering outer joins.