Concat multiple rows PSQL - sql

id | name | Subject | Lectured_Times | Faculty
3258132 | Chris Smith | SATS1364 | 10 | Science
3258132 | Chris Smith | ECTS4605 | 9 | Engineering
How would I go about creating the following
3258132 Chris Smith SATS1364, 10, Science + ECTS4605, 9,Engineering
where the + is just a new line. Notice how after the '+'(new line) it doesnt concat the id,name

try
SELECT distinct concat(id,"name",string_agg(concat(subject, Lectured_Times , Faculty), chr(10)))
from tn
where id = 3258132
group by id;

As mentioned above string_agg is perfect solution for this.
select
id, name, string_agg(concat(subject, Lectured_Times, Faculty), '\n')
from table
group by id, name

Related

Reorganize multiple rows in a new table with more columns

I have a table that looks like that:
+----------+----------+----------+----------+--------------------------+
| Club | Role | Name | Lastname | Email |
+----------+----------+----------+----------+--------------------------+
| Porto | 1 | Peter | Pan | peter.pan#mail.com |
| Porto | 2 | Michelle | Obama | michelle.obama#mail.com |
| Monaco | 1 | Serena | Williams | serena.williams#mail.com |
| Monaco | 2 | David | Beckham | david.beckham#mail.com |
+----------+----------+----------+----------+--------------------------+
and i want to get a table like that:
+----------+-----------------+-----------------+---------------------------------+-----------------+-----------------+---------------------------------+
| Club | Role 1 Name | Role 1 Lastname | Role 1 Email | Role 2 Name | Role 2 Lastname | Role 2 Email |
+----------+-----------------+-----------------+---------------------------------+-----------------+-----------------+---------------------------------+
| Porto | Peter | Pan | peter.pan#mail.com | Michelle | Obama | michelle.obama#mail.com |
| Monaco | Serena | Williams | serena.williams#mail.com | David | Beckham | david.beckham#mail.com |
+----------+-----------------+-----------------+---------------------------------+-----------------+-----------------+---------------------------------+
where the persons with different roles in each club puts in the same row.
I would ideally like to find a way to do that in Excel, but i am not sure if its possible. If not, SQL code would also help a lot.
Here is what I could come up with for an excel formula. Hopefully it can push you in the right direction.
This formula is assuming that your first table exists at the range A1:E5 and the second table exists at the range G1:M3. It is also assuming that the second table's column names are just repeating without the Role number attached to the front of it (same as the first table). This formula is an array formula, so you have to make sure to do CTRL+SHIFT+ENTER when inputting it.
{=INDEX($A$2:$E$5,
MATCH(1,($G2=$A$2:$A$5)*((FLOOR((COLUMN() - COLUMN($H$1) ) / 3,1) + 1)=$B$2:$B$5),0),
MATCH(H$1,$A$1:$E$1,0))}
The first part is using the INDEX forumla which pulls data from the range suppled ($A$2:$E$5) based on the row and column numbers supplied by the following MATCH formulas.
The first MATCH is supplying the row number for when the result of the lookup array section is equal to 1. I am checking two conditions, the first is to check for the "Club" name ($G2=$A$2:$A$5) and the second is to check for which "Role" we are currently on ((FLOOR((COLUMN() - COLUMN($H$1) ) / 3,1) + 1). This is using the FLOOR function to round the result down to the whole number and dividing by the number of columns (3 in this case: Name, Lastname, and Email).
The final MATCH is pulling the column number based on the header names from both tables. If you wanted to incorporate the changing names of the roles in the column headers, you could change this part to something like this:
{=INDEX($A$2:$E$5,
MATCH(1,($G2=$A$2:$A$5)*((FLOOR((COLUMN() - COLUMN($H$1) ) / 3,1) + 1)=$B$2:$B$5),0),
MOD(COLUMN() - COLUMN($H$1),3) + 3)}
I am adding 3 to the end of the mod because of the original range that was selected for table 1. The columns that we want to pull start at location 3 in the range.
If you want to do this in Oracle Sql, there's a nice approach in analytical sql.
To convert/swap rows to columns or columns to rows, we can use Pivot or Unpivot operators.
In you example use below query to covert data as you like,
select * from
(
with all_roles as
(select 1 role from dual union all
select 2 role from dual),
ddata as
(select 1 c_role, 'porto' club, 'peter' fname,'pan' lname,'peter.pan#mail.com' email from dual union all
select 2 c_role, 'porto' club, 'Michelle' fname, 'Obama' lname,'michelle.obama#mail.com' email from dual union all
select 1 c_role, 'monaco' club, 'Serena' fname, 'Williams' lname,'serena.williams#mail.com' email from dual union all
select 2 c_role, 'monaco' club, 'David' fname, 'Beckham' lname,'david.beckham#mail.com' email from dual )
(select role, club, fname,lname, email from ddata,all_roles
where all_roles.role=ddata.c_role)) all_data
pivot (
max(fname) fname,
max(lname) lname,
max(email) email
for role in ( 1 role1, 2 role2 )
)
order by club;

How to get initials easily out of text field using Postgres

I am using Postgres version 9.4 and I have a full_name field in a table.
In some cases, I want to put initials instead of the full_name of the person in my table.
Something like:
Name | Initials
------------------------
Joe Blow | J. B.
Phil Smith | P. S.
The full_name field is a string value (obviously) and I think the best way to go about this is to split the string into an array foreach space i.e.:
select full_name, string_to_array(full_name,' ') initials
from my_table
This produces the following result-set:
Eric A. Korver;{Eric,A.,Korver}
Ignacio Bueno;{Ignacio,Bueno}
Igmar Mendoza;{Igmar,Mendoza}
Now, the only thing I am missing is how to loop through each array element and pull the 1st character out of it. I will end up using substring() to get the initial character of each element - however I am just stuck on how to loop through them on-the-fly..
Anybody have a simple way to go about this?
Use unnest with string_agg:
select full_name, string_agg(substr(initials, 1,1)||'.', ' ') initials
from (
select full_name, unnest(string_to_array(full_name,' ')) initials
from my_table
) sub
group by 1;
full_name | initials
------------------------+-------------
Phil Smith | P. S.
Joe Blow | J. B.
Jose Maria Allan Pride | J. M. A. P.
Eric A. Korver | E. A. K.
(4 rows)
In Postgres 14+ you can replace unnest(string_to_array(...)) with string_to_table(...).
Test it in db<>fiddle.
You can also create a helper function for this, in case you want to use similar logic in multiple queries. Check this out
--
-- Function to extract a person's initials from the full name.
--
DROP FUNCTION IF EXISTS get_name_initials(TEXT);
CREATE OR REPLACE FUNCTION get_name_initials(full_name TEXT)
RETURNS TEXT AS $$
DECLARE
result TEXT :='';
part VARCHAR :='';
BEGIN
FOREACH part IN ARRAY string_to_array($1, ' ') LOOP
result := result || substr(part, 1, 1) || '.';
END LOOP;
RETURN result;
END;
$$ LANGUAGE plpgsql;
Now you can simply use this function to get the initials like this.
SELECT full_name, get_name_initials(full_name) as initials
FROM my_table;
SELECT get_name_initials('Phil Smith'); -- Returns P. H.
SELECT get_name_initials('Joe Blow'); -- Returns J. B.
SqlFiddleDemo
WITH add_id AS (
SELECT n.*, row_number() OVER (ORDER BY "Name") AS id
FROM names n
),
split_names AS (
SELECT id, regexp_split_to_table("Name", E'\\s+') AS single_name
FROM add_id
),
initials AS (
SELECT id, left(single_name, 1) || '.' AS initial
FROM split_names
),
final AS (
SELECT id, string_agg(initial, ' ')
FROM initials
GROUP BY id
)
SELECT a.*, f.*
FROM add_id a
JOIN final f USING (id)
For debug I create the Initial to Show how match the string_agg
| Name | Initials | id | id | string_agg |
|----------------|----------|----|----|------------|
| Eric A. Korver | E. A. K. | 1 | 1 | E. A. K. |
| Igmar Mendoza | I. M. | 2 | 2 | I. M. |
| Ignacio Bueno | I. B. | 3 | 3 | I. B. |
| Joe Blow | J. B. | 4 | 4 | J. B. |
| Phil Smith | P. S. | 5 | 5 | P. S. |
After some work I got a compact version SqlFiddleDemo
SELECT "Name", string_agg(left(single_name, 1) || '.', '') AS Initials
FROM (
SELECT
"Name",
regexp_split_to_table("Name", E'\\s+') AS single_name
FROM names
) split_names
GROUP BY "Name"
OUTPUT
| Name | initials |
|----------------|----------|
| Eric A. Korver | E.K.A. |
| Igmar Mendoza | M.I. |
| Ignacio Bueno | I.B. |
| Joe Blow | B.J. |
| Phil Smith | P.S. |

Rows which do not exist in a table

I have a lists of names John, Rupert, Cassandra, Amy, and I want to get names which are not exists in table: Cassandra, Amy
How should I write such query?
My table:
+----+--------+-----------+------+
| id | name | address | tele |
+----+--------+-----------+------+
| 1 | Rupert | Somewhere | 022 |
| 2 | John | Doe | 029 |
| 3 | Donald | Armstrong | 021 |
| 4 | Bob | Gates | 022 |
+----+--------+-----------+------+
Think in sets. You add names to a the result set with UNION ALL, you remove names from the result set with EXCEPT.
select 'John'
union all
select 'Rupert'
union all
select 'Cassandra'
union all
select 'Amy'
except
select name from mytable;
Build up a list of your names to check and do a left join to the users table:
with to_check (name) as (
values
('John'), ('Rupert'), ('Cassandra'), ('Amy')
)
select tc.name as missing_name
from to_check tc
left join the_table tt on tt.name = tc.name
where tt.name is null;
SQLFiddle example: http://sqlfiddle.com/#!15/5c4f5/1
Hope your list is in form of table lets its be table b and your original table as a
now SQL query goes like it
Select name from a where name not in (select name from b);
Think this will give you solution as per my understanding. Also if further details are required please comment.
Also its more important to search for an answer as it look like its a question from a book/Class. Please try out to find solution could have got much more information like link below
How to write "not in ()" sql query using join

Several conditions on the same table

I've inherited an excel file converted to a database and I try to figure out the people who went to several locations.
| Customer | email | ZIP | shop |
| John Smith | js#mail.com | 75016 | 1 |
| Mary King | mary#ymail.com | 97430 | 2 |
| John Smith | js#mail.com | 75016 | 3 |
| Ivan Turtle | ivan#mail.com | 56266 | 5 |
| Mary King | mary#ymail.com | 97430 | 5 |
| John Smith | js#mail.com | 75016 | 5 |
Eg : John Smith had been to 1, 3, 5
Mary King to 2, 3
I tried to use email as a key but can't figure out how to solve this one
assuming each row specifies a separate location,
select customer, count(*) as countOfLocation
from TABLE
group by customer
having count(*) > 1
order by countOfLocation desc
Is the simplest answer
I believe something like this will work for you.
Find all customers who have been to more than one shop, and then join back to get the details of each customer.
This will give back multiple rows per person, one for each shop they have been to. If all you care about is the names of customers, you can simply use the second SELECT
If you want one row per customer and a CSV of the shops, you will have to do a bit of joining.
SELECT a.*
FROM Customers a
INNER JOIN
-- Find all customers who have been to more than one shop.
(SELECT email
FROM Customers
GROUP BY email
HAVING COUNT(shop) > 1) b
ON a.email = b.email
Try with this if is Sql server:
select customer, count(distinct(shop))
from table
group by customer, shop
having count(distinct(shop)) > 1
or, you only want to know how many places they visited:
select customer, count(distinct(shop))
from table
group by customer, shop
if you want to show the user name and shop seperating by comma in one row use this
SELECT Customer
,STUFF((SELECT ', ' + CAST(shop AS VARCHAR(10)) [text()]
FROM 'ur table'
WHERE customer = t.customer
FOR XML PATH(''), TYPE)
.value('.','NVARCHAR(MAX)'),1,2,' ') List_Output
FROM 'ur table' t
GROUP BY customer

Splitting a string column in BigQuery

Let's say I have a table in BigQuery containing 2 columns. The first column represents a name, and the second is a delimited list of values, of arbitrary length. Example:
Name | Scores
-----+-------
Bob |10;20;20
Sue |14;12;19;90
Joe |30;15
I want to transform into columns where the first is the name, and the second is a single score value, like so:
Name,Score
Bob,10
Bob,20
Bob,20
Sue,14
Sue,12
Sue,19
Sue,90
Joe,30
Joe,15
Can this be done in BigQuery alone?
Good news everyone! BigQuery can now SPLIT()!
Look at "find all two word phrases that appear in more than one row in a dataset".
There is no current way to split() a value in BigQuery to generate multiple rows from a string, but you could use a regular expression to look for the commas and find the first value. Then run a similar query to find the 2nd value, and so on. They can all be merged into only one query, using the pattern presented in the above example (UNION through commas).
Trying to rewrite Elad Ben Akoune's answer in Standart SQL, the query becomes like this;
WITH name_score AS (
SELECT Name, split(Scores,';') AS Score
FROM (
(SELECT * FROM (SELECT 'Bob' AS Name ,'10;20;20' AS Scores))
UNION ALL
(SELECT * FROM (SELECT 'Sue' AS Name ,'14;12;19;90' AS Scores))
UNION ALL
(SELECT * FROM (SELECT 'Joe' AS Name ,'30;15' AS Scores))
))
SELECT name, score
FROM name_score
CROSS JOIN UNNEST(name_score.score) AS score;
And this outputs;
+------+-------+
| name | score |
+------+-------+
| Bob | 10 |
| Bob | 20 |
| Bob | 20 |
| Sue | 14 |
| Sue | 12 |
| Sue | 19 |
| Sue | 90 |
| Joe | 30 |
| Joe | 15 |
+------+-------+
If someone is still looking for an answer
select Name,split(Scores,';') as Score
from (
# replace the inner custome select with your source table
select *
from
(select 'Bob' as Name ,'10;20;20' as Scores),
(select 'Sue' as Name ,'14;12;19;90' as Scores),
(select 'Joe' as Name ,'30;15' as Scores)
);