mysql GROUP_CONCAT duplicates - sql

I make my join from a farmTOanimal table like this. There is a similar farmTotool table
id | FarmID | animal
1 | 1 | cat
2 | 1 | dog
When I join my tables in a view, I get a result that looks like this
FarmID | animal | tool
1 | cat | shovel
1 | dog | shovel
1 | cat | bucket
1 | dog | bucket
Now, I do GROUP BY FarmID, and GROUP_CONCAT(animal) and GROUP_CONCAT(tool), i get
FarmID | animals | tools
1 | cat,dog,cat,dog | shovel,shovel,bucket,bucket
But, what I really want is a result that looks like this. How can I do it?
FarmID | animals | tools
1 | cat,dog | shovel,bucket

You need to use the DISTINCT option:
GROUP_CONCAT(DISTINCT animal)

Related

How to select table with a concatenated column?

I have the following data:
select * from art_skills_table;
+----+------+---------------------------+
| ID | Name | skills |
+----+------+---------------------------|
| 1 | Anna | ["painting","photography"]|
| 2 | Bob | ["drawing","sculpting"] |
| 3 | Cat | ["pastel"] |
+----+------+---------------------------+
select * from computer_table;
+------+------+-------------------------+
| ID | Name | skills |
+------+------+-------------------------+
| 1 | Anna | ["word","typing"] |
| 2 | Cat | ["code","editing"] |
| 3 | Bob | ["excel","code"] |
+------+------+-------------------------+
I would like to write an SQL statement which results in the following table.
+------+------+-----------------------------------------------+
| ID | Name | skills |
+------+------+-----------------------------------------------+
| 1 | Anna | ["painting","photography","word","typing"] |
| 2 | Bob | ["drawing","sculpting","excel","code"] |
| 3 | Cat | ["pastel","code","editing"] |
+------+------+-----------------------------------------------+
I've tried something like SELECT * from art_skills_table LEFT JOIN computer_table ON name. However it doesn't give what I need. I've read about array_cat but I'm having a bit of trouble implementing it.
if the skills column from both tables are arrays, then you should be able to get away with this:
SELECT a.ID, a.name, array_cat(a.skills, c.skills)
FROM art_skills_table a LEFT JOIN computer_table c
ON c.id = a.id
That said, While you used LEFT join in your sample, I think either an INNER or FULL (OUTER) join might serve you better.
First, i wondered why the data are stored in such a model.
Was of the opinion that NoSQL databases lack ability for joins and ...
... a semantic triple would be in the form of subject–predicate–object.
... a Key-value (KV) stores use associative arrays.
... a relational database would be normalized.
A few information about the use case would have helped.
Nevertheless, you can select the data with CONCAT and REPLACE for the desired form.
SELECT art_skills_table.ID, computer_table.name,
CONCAT(
REPLACE(art_skills_table.skills, '}',','),
REPLACE(computer_table.skills, '{','')
)
FROM art_skills_table JOIN computer_table ON art_skills_table.ID = computer_table.ID
The query returns the following result:
+----+------+--------------------------------------------+
| ID | Name | Skills |
+----+------+--------------------------------------------+
| 1 | Anna | {"painting","photography","word","typing"} |
| 2 | Cat | {"drawing","sculpting","code","editing"} |
| 3 | Bob | {"pastel","excel","code"} |
+----+------+--------------------------------------------+
I've used the ID for the JOIN, even though Bob has different values.
The JOIN should probably be done over the name.
JOIN computer_table ON art_skills_table.Name = computer_table.Name
BTW, you need to tell us what SQL engine you're running on.

How to use ConcatRelated() on a many-to-many relationship

As a personal education experiment, I am building a database for an imaginary animal shelter. I am quite new to SQL and databases in general.
Task: Finding all the owners of a pet in an animal shelter database.
I have three tables: Pets, Owners, and Pets2Owners. Owners can have one or many pets, and pets can have one, many, or no owners. The tables look like this:
Pets
+--------+----------+
| ID | Name |
+--------+----------+
| 1 | Opal |
| 2 | Fuzzy |
| 3 | Angel |
| 4 | Peewee |
| 5 | Spike |
+--------+----------+
Owners
+----------+--------+
| ID | Name |
+----------+--------+
| 1 | Shy |
| 2 | Belle |
| 3 | Velvet |
| 4 | Brilla |
+----------+--------+
Pets2Owners
+----+--------+----------+
| ID | Pet_ID | Owner_ID |
+----+--------+----------+
| 1 | 1 | 2 |
| 2 | 2 | 1 |
| 3 | 3 | 1 |
| 4 | 5 | 3 |
| 5 | 5 | 4 |
+----+--------+----------+
So, from Pets2Owners, we see that Pet #3 (Angel) is owned by Owner #1 (Ms. Shy), Pet #5 (Spike) is owned by both Owners #3 & 4 (Velvet and Brilla), and Pet #4 (Peewee) is owned by no one.
I an inexperienced in SQL, and I'm not sure this is possible, but is there a way to make a single query that gives me all the information on pets and owners without duplicating pets, like this:
+--------+--------+
| Pets | Owners |
+--------+--------+
| Opal | Belle |
| Fuzzy | Shy |
| Angel | Shy |
| Peewee | (none) |
| Spike | Velvet |
| | Brilla |
+--------+--------+
Essentially, I want a way to combine information from all three tables into one. I have tried using joins, but they leave me with duplicate entries (e.g. two rows for Spike, one with the owner Velvet, the other with the owner Brilla).
Alternately, is it possible to make a query that would return all the owners of a particular pet, like so:
Spike's Owners
+--------+
| Owners |
+--------+
| Velvet |
| Brilla |
+--------+
I could use this to accomplish the same goal and have all the owners of a particular pet listed (although I'm not sure if SQL has variables that would let me make a single query for a variable PetName, then repeat it for a arbitrary name)
You can use Allen Browne's function ConcatRelated() to create a single row like this:
| Spike | Velvet, Brilla |
ConcatRelated() is designed for one-to-many relationships. Since you have many-to-many, you need an intermediate query that combines Pet IDs with Owner names:
SELECT Pets2Owners.Pet_ID, Owners.OwnerName
FROM Pets2Owners INNER JOIN Owners ON Pets2Owners.Owner_ID = Owners.ID;
I called this query Pet_OwnerNames.
Then ConcatRelated() is straightforward:
SELECT Pets.PetName,
ConcatRelated("OwnerName", "Pet_OwnerNames", "Pet_ID = " & ID) AS Owners
FROM Pets;
Result:
+---------+----------------+
| PetName | Owners |
+---------+----------------+
| Opal | Belle |
| Fuzzy | Shy |
| Angel | Shy |
| Peewee | |
| Spike | Velvet, Brilla |
+---------+----------------+
SELECT Pets.Name AS Pets, Owners.Name AS Owner
FROM Pets2Owner INNER JOIN Pets ON Pets2Owner.[Pet Id] = Pets.[id]
INNER JOIN Owners ON Pets2Owner.[Owner Id] = Owners.[id]
after you get a good grip of the JOINS,
try to look at parameters for Access :)

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'

How to update the PK of every record

I have two tables that are exactly the same, except for one column. Both tables have the same amount of records and the same amount of columns, and all of the data is the same.
Table A:
+--------------------------------------+--------+--------+---------+
| GUID | Animal | Person | Vehicle |
+--------------------------------------+--------+--------+---------+
| 1D001609-7071-4DBB-9E65-0000B3EEF751 | cat | matt | car |
| 90260783-E3C3-4A9B-BEA0-000388EA41E1 | dog | rich | truck |
| DD18FCFA-99BD-4FBC-AFC2-00058EF95D0A | zebra | alex | van |
+--------------------------------------+--------+--------+---------+
Table B:
+--------------------------------------+--------+--------+---------+
| GUID | Animal | Person | Vehicle |
+--------------------------------------+--------+--------+---------+
| F67A3079-8589-4304-AA3C-000688696BAA | cat | matt | car |
| C71710EC-492F-424E-805D-00068AFE4E82 | dog | rich | truck |
| 5F830142-F4CC-4580-974D-000710F1AB5F | zebra | alex | van |
+--------------------------------------+--------+--------+---------+
I need the GUIDs of table A to be updated to equal the GUIDs of Table B. Something like this will work:
update a
set a.guid=b.guid
from tablea a
join tableb b
on a.animal=b.animal
and a.person=b.person
and a.vehicle=b.vehicle
But the above solution will not work for me. I have actually approximately 50 columns and about 1 million records.
Would you have any other suggestions on how to update the GUID in table A more efficiently?
UPDATE A
SET [GUID] = (
SELECT [GUID]
FROM B
WHERE EXISTS (
SELECT A.Animal,A.Person,A.Vehicle
INTERSECT
SELECT B.Animal,B.Person,B.Vehicle
)
)
If you want the tables identical, how about this:
DROP TABLE B
SELECT *
INTO B
FROM A

Grouped string aggregation / LISTAGG for SQL Server

I'm sure this has been asked but I can't quite find the right search terms.
Given a schema like this:
| CarMakeID | CarMake
------------------------
| 1 | SuperCars
| 2 | MehCars
| CarMakeID | CarModelID | CarModel
-----------------------------------------
| 1 | 1 | Zoom
| 2 | 1 | Wow
| 3 | 1 | Awesome
| 4 | 2 | Mediocrity
| 5 | 2 | YoureSettling
I want to produce a dataset like this:
| CarMakeID | CarMake | CarModels
---------------------------------------------
| 1 | SuperCars | Zoom, Wow, Awesome
| 2 | MehCars | Mediocrity, YoureSettling
What do I do in place of 'AGG' for strings in SQL Server in the following style query?
SELECT *,
(SELECT AGG(CarModel)
FROM CarModels model
WHERE model.CarMakeID = make.CarMakeID
GROUP BY make.CarMakeID) as CarMakes
FROM CarMakes make
http://www.simple-talk.com/sql/t-sql-programming/concatenating-row-values-in-transact-sql/
It is an interesting problem in Transact SQL, for which there are a number of solutions and considerable debate. How do you go about producing a summary result in which a distinguishing column from each row in each particular category is listed in a 'aggregate' column? A simple, and intuitive way of displaying data is surprisingly difficult to achieve. Anith Sen gives a summary of different ways, and offers words of caution over the one you choose...
If it is SQL Server 2017 or SQL Server VNext, Azure SQL database you can use String_agg as below:
SELECT make.CarMakeId, make.CarMake,
CarModels = string_agg(model.CarModel, ', ')
FROM CarModels model
INNER JOIN CarMakes make
ON model.CarMakeId = make.CarMakeId
GROUP BY make.CarMakeId, make.CarMake
Output:
+-----------+-----------+---------------------------+
| CarMakeId | CarMake | CarModels |
+-----------+-----------+---------------------------+
| 1 | SuperCars | Zoom, Wow, Awesome |
| 2 | MehCars | Mediocrity, YoureSettling |
+-----------+-----------+---------------------------+