How To Remove a Second Id From JOIN In SQL - mysql

I'm new to programming in SQL, and I need help on removing the second 1 through 5 id's on the right from my output (the numbers are highlighted in bold):
ID
Name
Gender
ID
Country
1
Abdur-Rahman
M
1
America
2
Don Madden
M
2
England
3
Dustin Tompkins
M
3
America
4
Nicki Harris
F
4
Germany
5
Samantha Harris
F
5
France
CREATE TABLE test ( id INTEGER PRIMARY KEY, name TEXT);
INSERT INTO test VALUES (1,"Albert Franco");
INSERT INTO test VALUES (2,"Don Madden");
INSERT INTO test VALUES (3,"Dustin Tompkins");
INSERT INTO test VALUES (4,"Nicki Harris");
INSERT INTO test VALUES (5,"Samantha Harris");
ALTER TABLE test
ADD COLUMN gender TEXT;
UPDATE test
SET gender = 'M'
WHERE id = 1;
UPDATE test
SET gender = 'M'
WHERE id = 2;
UPDATE test
SET gender = 'M'
WHERE id = 3;
UPDATE test
SET gender = 'F'
WHERE id = 4;
UPDATE test
SET gender = 'F'
WHERE id = 5;
CREATE TABLE country (
id INTEGER,
nation TEXT
);
INSERT INTO country VALUES (1,"America");
INSERT INTO country VALUES (2,"England");
INSERT INTO country VALUES (3,"America");
INSERT INTO country VALUES (4,"Germany");
INSERT INTO country VALUES (5,"France");
SELECT * FROM test
JOIN country
ON test.id = country.id;

To actually answer your question, you should explicitly state the columns you want, i.e.
SELECT t.id, t.name, t.gender, c.nation
FROM test AS t
JOIN country AS c
ON c.id = t.id;
It is however worth noting that your schema doesn't really make sense, you have to duplicate countries, which is not normalised. You've simply created a 1:1 relationship, you'd be as well just adding a nation column to test.
A better solution though would be to normalise the data, so your country table would become:
CREATE TABLE Country
(
Id INT AUTO_INCREMENT PRIMARY KEY,
Nation VARCHAR(50)
);
INSERT INTO Country(Nation)
VALUES ('America'), ('England'), ('France'), ('Germany');
Then in your Test Table, add CountryId as foreign key to your country table:
CREATE TABLE Test
(
Id INT AUTO_INCREMENT PRIMARY KEY,
Name VARCHAR(500) NOT NULL,
Gender CHAR(1) NOT NULL,
CountryId INT NOT NULL,
CONSTRAINT FK_Test__CountryId FOREIGN KEY (CountryID) REFERENCES Country (Id)
);
INSERT INTO Test (Name, Gender, CountryId)
SELECT data.Name, data.Gender, c.Id
FROM (
SELECT 'Albert Franco' AS Name, 'M' AS Gender, 'America' AS Nation
UNION ALL
SELECT 'Don Madden' AS Name, 'M' AS Gender, 'England' AS Nation
UNION ALL
SELECT 'Dustin Tompkins' AS Name, 'M' AS Gender, 'America' AS Nation
UNION ALL
SELECT 'Nicki Harris' AS Name, 'F' AS Gender, 'Germany' AS Nation
UNION ALL
SELECT 'Samantha Harris' AS Name, 'F' AS Gender, 'France' AS Nation
) AS data
INNER JOIN Country AS c
ON c.Nation = data.Nation;
Your final query is largely similar:
SELECT t.Id, t.Name, t.Gender, c.Nation
FROM Test AS t
INNER JOIN Country AS c
ON c.Id = t.CountryId;
But you have now normalised your countries, so America only appears once. Obviously in a simple example this only saves you one row, but if you have a lot of names, this has saved a lot of duplication, and potential for error. With free type entry (e.g. having a nation column in test) you inevitably end up with multiple variations of everything, e.g. "America", "USA", "U.S.A", "US", "U.S", "United States", this doesn't even consider typos! All of this leads to headaches down the road.
Full Example on SQL Fiddle
*N.B. There's a pretty good argument that the country table should not have a surrogate primary key (AUTO_INCREMENT) and it should instead use the ISO country code. The natural vs surrogate keys debate has been going on for years and years, and is well beyond the scope of this answer

If I understood your question correctly, you want something like this
Then you need to write your query like this
Select test.*, country.nation from test join country on test.id=country.id;
Now, you will only receive the id from your test table

Related

Display students name and score in mathematics( even if they don t have a score )

Student table: id, name, class
Score table: student_id, score, discipline
i tried:
CREATE TABLE STUDENT(
ID int NOT NULL auto_increment PRIMARY KEY,
Name varchar(225),
Class varchar(225)
);
CREATE TABLE SCORES(
student_id int,
FOREIGN KEY (student_id) REFERENCES STUDENT(ID) ON DELETE CASCADE ON UPDATE CASCADE,
Score int,
Discipline varchar(225)
);
Select distinct st.name ,
case when sc.discipline='Mathematics' then sc.score
else 'NO SCORE'
end as Mathematics_score
from STUDENT st left join SCORES sc on st.id=sc.student_id ;
result:
name Mathematics_score
Liam NO SCORE
ANIA NO SCORE
ALEX NO SCORE
MAX 7
MAX NO SCORE
ANIA 9
ABC NO SCORE
What i need to change to show just once a name if they have grade for mathematics. for example MAX has grade to mathematics and Biology and he appear twice in table
Thank you and sorry for my english!
Try this out:
Select distinct st.name, Coalesce(sc.score,"No Score") as Mathematics_score
from STUDENT st left join SCORES sc
on st.id=sc.student_id and
sc.discipline='Mathematics'
The join will return exactly one row for each student, either Student Name| Score or Student Name | NULL. Coalesce then replaces the NULL with 'No Score'
Use the SQL RANK function and pick rows with rank value 1.
it should do the trick.
Check following link for details
SQL Rank Function Detail Link

Group by wether or not the cell exists in another table as well

My SQL syntax is MariaDB (MySQL)
I have a table with organisation spokepersons, and a table with VIP organizations, and a table with presentations. How do I group or sort by wether the spokeperson's organisation is VIP, so that VIP organisation spokepersons show up on top when retrieving all presentations?
Table presentations: int presentation_id, int person_id, varchar title, date date
Table persons: int person_id, varchar name, varchar function, varchar organisation
Table VIP_orgs: int org_id, varchar org_name
Query that doesn't work:
CREATE TABLE persons (
person_id INT AUTO_INCREMENT,
name VARCHAR(64),
organisation VARCHAR(64),
PRIMARY KEY (person_id)
);
INSERT INTO `persons` (name, organisation) VALUES
("Guy Fieri", "VIP-org"),
("Fiona", "VIP inc."),
("Mr. Robot", "Evil Corp"),
("Marcus Antonius", "Rome"),
("Cicero", "Rome"),
("Shrek", "VIP inc.");
CREATE TABLE presentations (
presentation_id INT AUTO_INCREMENT,
person_id INT,
PRIMARY KEY (presentation_id)
);
INSERT INTO `presentations` (person_id) VALUES
(1),(1),(1),(1), -- guy fieri has 4
(2),
(3),(3),(3),(3),(3),
(4),(4),(4),(4),
(5),(5),(5),
(6),(6),(6),(6);
CREATE TABLE VIP_orgs (
org_id INT AUTO_INCREMENT,
org_name VARCHAR(64),
PRIMARY KEY (org_id)
);
INSERT INTO `VIP_orgs` (org_name) VALUES
("VIP-org"),
("VIP inc.");
SELECT organisation, COUNT(*) AS count
FROM `presentations`
JOIN `persons` ON `presentations`.person_id = `persons`.person_id
GROUP BY (SELECT org_name FROM `VIP_orgs` WHERE `VIP_orgs`.org_name = organisation), organisation
ORDER BY count DESC;
What I expect it to do:
return a table org_name, (total combined number of presentations by each spokeperson of that org)
Sorted by count of presentations, grouped by organisation, VIP organisations grouped on top.
The VIP and non-VIP parts should be sorted by count independently. The returned table should thus look something like this:
name count
VIP inc. 5
VIP-org 4
Rome 7
Evil Corp 5
The query works 50%: it counts all presentations and sorts it, but it doesn't seem to group by VIP organizations. In actuality the returned table looks like this:
name count
Rome 7
VIP inc. 5
Evil Corp 5
VIP-org 4
The schema doesn't look right. I would suggest creating an organisations table with a vip BOOLEAN column and add foreign key in persons table. Make the following changes in the schema:
CREATE TABLE `organisations` (
organisation_id INT AUTO_INCREMENT,
name VARCHAR(64),
vip BOOLEAN,
PRIMARY KEY (organisation_id)
);
INSERT INTO `organisations` (name, vip) VALUES
("VIP-org", True),
("VIP inc.", True),
("Evil Corp", False),
("Rome", False);
CREATE TABLE persons (
person_id INT AUTO_INCREMENT,
name VARCHAR(64),
organisation_id INT,
PRIMARY KEY (person_id),
FOREIGN KEY (organisation_id) REFERENCES `organisations`(organisation_id)
);
INSERT INTO `persons` (name, organisation_id) VALUES
("Guy Fieri", 1),
("Fiona", 2),
("Mr. Robot", 3),
("Marcus Antonius", 4),
("Cicero", 4),
("Shrek", 2);
Now the query would look something like this:
SELECT `organisations`.name as organisation, COUNT(*) AS count
FROM `presentations`
JOIN `persons` ON `presentations`.person_id = `persons`.person_id
JOIN `organisations` ON `organisations`.organisation_id = `persons`.organisation_id
GROUP BY `organisations`.organisation_id
ORDER BY `organisations`.vip DESC, count DESC;
Output:
+--------------+------------+
| organisation | count |
+--------------+------------+
| VIP inc. | 5 |
| VIP-org | 4 |
| Rome | 7 |
| Evil Corp | 5 |
+--------------+------------+
You can see the result here: db <> fiddle
Instead of grouping by, I needed to sort. DOh!
Edit: this doesn't quite work. It does not sort by count. If I put the ORDER BY count clause first, it puts all vip orgs on the bottom.
Edit 2: using EXISTS, it seems to work
SELECT organisation, COUNT(*) AS count
FROM `presentations`
JOIN `persons` ON `presentations`.person_id = `persons`.person_id
GROUP BY organisation
ORDER BY EXISTS (SELECT org_name FROM `VIP_orgs` WHERE `VIP_orgs`.org_name = organisation) DESC, count DESC;

calculate import-export

Given a company table companylist and a import-export table trades, I want to find the total exports and imports per country. I want to sort them by country name and print 0s instead of nulls if exports/imports are 0. All countries need to be present in output.
Companylist table =>
name country
abc corp congo
arcus t.g. ghana
bob timbuktu
ddr ltd ghana
none at all nothingland
xyz corp bubbleland
Y zap timbuktu
trades table
id seller buyer value
20120125 bob arcus t.g. 100
20120216 abc corp ddr ltd 30
20120217 abc corp ddr ltd 50
20121107 abc corp bob 10
20123112 arcus t.g. Y zap 30
The tables DDL -
create table if not exists companylist (name varchar(30) not null, country varchar(30) not null, unique(name));
truncate table companylist;
create table if not exists trades (id integer not null, seller varchar(30) not null, buyer varchar(30) not null, value integer
not null, unique(id));
truncate table trades;
insert into companylist(name,country) values ('bob','timbuktu');
insert into companylist(name,country) values ('Y zap','timbuktu');
insert into companylist(name,country) values ('ddr ltd','ghana');
insert into companylist(name,country) values ('arcus t.g.','ghana');
insert into companylist(name,country) values ('abc corp','congo');
insert into companylist(name, country) values ('xyz corp', 'bubbleland');
insert into companylist(name,country) values ('none at all','nothingland');
insert into trades(id,seller,buyer,value) values (20121107,'abc corp','bob',10);
insert into trades(id,seller,buyer,value) values (20123112,'arcus t.g.','Y zap',30);
insert into trades(id,seller,buyer,value) values (20120125,'bob','arcus t.g.',100);
insert into trades(id,seller,buyer,value) values (20120216,'abc corp','ddr ltd',30);
insert into trades(id,seller,buyer,value) values (20120217,'abc corp','ddr ltd',50);
So far i have
select name, country, coalesce(sum(b.value),0) as export, coalesce(sum(c.value),0) as import
from companylist a left join trades b on a.name = b.seller
left join trades c on a.name = c.buyer
group by a.country order by a.country ASC;
I think this works, but does someone have a more elegant / better / different solution? I am learning sql so any feedback helps.
Conditional aggregation is an option:
SELECT country,
SUM(CASE WHEN a.name = b.seller
THEN b.value
ELSE 0
END) as export,
SUM(CASE WHEN a.name = b.buyer
THEN b.value
ELSE 0
END) as import
FROM companylist a
LEFT join trades b ON a.name IN (b.seller, b.buyer)
GROUP BY a.country
ORDER BY a.country ASC;
fiddle
PS. companylist.name makes no sense in output - it was removed.
I would recommend union all and group by:
select c.country, sum(sales) as exports, sum(buys) as imports
from countrylist c left join
((select t.seller as name, value as sales, 0 as buys
from trades t
) union all
(select t.buyer, 0 as sales, value as buys
from trades t
)
) bs
on c.name = bs.name
group by c.country;
This should be much more efficient than Akina's solution which uses a function in the on clause (essentially an or).

Some selections I don't know how to write [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 3 years ago.
Improve this question
I'm new to SQL, so please forgive me if I ask stupid questions.
I have three tables, one with countries, one with cities (connected to countries, with populations...) and one with languages (also connected to countries).
I'd like to ask MySQL about the following informations:
the names of the countries for which all the cities have more than 100000 citizen,
the names of the countries for which at least one city is in the cities table,
the names of the countries where english is spoken, but not spanish,
and so on. I start to understand junctures, a little bit of grouping, but that's about all.
First query
SELECT name FROM country WHERE id IN
(SELECT big_city.country_id FROM
(SELECT country_id, COUNT(*) as n FROM city WHERE population > 100000 GROUP BY country_id) as big_city,
(SELECT country_id, COUNT(*) as n FROM city GROUP BY country_id)
as all_city WHERE big_city.country_id = all_city.country_id AND big_city.n = all_city.n)
What I'm doing here in the subqueries is making a list of countries with all registered towns having a population greater than 100'000 people.
Second query
SELECT country.name FROM country WHERE country.id IN (SELECT DISTINCT country_id FROM city);
Doing so you will get all the country IDs in the city table, so you can use this as a condition
Third query
SELECT country.name FROM country WHERE country.id IN
(SELECT DISTINCT country_id FROM language WHERE language = "en")
AND NOT country.id IN (SELECT DISTINCT country_id FROM language WHERE language = "es")
Same as before, you fetch all the countries in which English or Spanish is spoken and you filter accordingly
Provide more details like tables and database version:
If you consider the following script in SQLServer 2017:
create table countries
(
[id] int not null identity,
[name] varchar(100) not null,
constraint pk_countries primary key ([id])
)
insert into countries ([name]) values ('Country 1'), ('Country 2'), ('Country 3')
create table cities
(
[id] int not null identity,
[idCountry] int not null,
[name] varchar(100) not null,
[population] int not null,
constraint pk_cities primary key ([id]),
constraint fk_cities_countries foreign key ([idCountry]) references countries ([id])
)
insert into cities ([idCountry], [name], [population]) values
(1, 'City 11', 1500000), (1, 'City 22', 2000000),
(2, 'City 21', 2000000), (2, 'City 22', 100)
create table languages
(
[id] int not null identity,
[idCity] int not null,
[name] varchar(100) not null,
constraint pk_languages primary key ([id]),
constraint fk_languages_cities foreign key ([idCity]) references cities ([id])
)
insert into languages ([idCity], [name]) values (1, 'Lang 1'), (1, 'Lang 2'), (1, 'Lang 3')
-- the names of the countries for which all the cities have more than 100000 citizen
select
distinct (a.name)
from
countries a
where
not exists (select * from cities where idCountry = a.id and population < 1000000) and
exists (select * from cities where idCountry = a.id)
go
-- the names of the countries for which at least one city is in the cities table,
select
distinct (a.name)
from
countries a
where
exists (select * from cities where idCountry = a.id)
Results (http://www.sqlfiddle.com/#!18/326e0/1):
Country1
Country 1
Country 2

Get Count of different values in comma separated row in mysql

A table Jobs which have 2 column JobId, City when we save job a job location may be multiple city like below
-----------------------------
JobId City
-------------------------------
1 New York
2 New York , Ohio , Virginia
3 New York , Virginia
how i count jobid in perticular city like i want count of jobid in New York city
i want result like
New York 3
Ohio 1
Virginia 2
Your database is poorly designed and you are going to have a lot of trouble down the line.
Using the current structure you can get the count using the find_in_set function but that you should avoid .
Your table is as
create table test
(jobid int ,city varchar(100));
insert into test values
(1,'New York'),
(2,'New York, Ohio,Virginia'),
(3,'New York,Virginia');
Now to get the count you can use the following
select
count(*) as tot from test
where
find_in_set('Virginia',city) > 0;
As I mentioned this is a poor db design the ideal would be as
first a job table with job details
a location table containing all the locations
and finally a table linking a job and a location
So it would look like
create table jobs (jobid int, name varchar(100));
insert into jobs values
(1,'PHP'),(2,'Mysql'),(3,'Oracle');
create table locations (id int, name varchar(100));
insert into locations values (1,'New York'),(2,'Ohio'),(3,'Virginia');
create table job_locations (id int, jobid int, location_id int);
insert into job_locations values
(1,1,1),(2,2,1),(3,2,2),(4,2,3),(5,3,1),(6,3,3);
Now getting the count and many more operations will be fairly easy
select
count(j.jobid) as tot
from jobs j
join job_locations jl on jl.jobid = j.jobid
join locations l on l.id = jl.location_id
where
l.name = 'Virginia'
For counting all the jobs per city and using the above schema it would very simple
select
l.name,
count(j.jobid) as tot
from jobs j
join job_locations jl on jl.jobid = j.jobid
join locations l on l.id = jl.location_id
group by l.name
DEMO
SELECT
SUBSTRING_INDEX(SUBSTRING_INDEX(all_city, ',', num), ',', -1) AS one_city,
COUNT(*) AS cnt
FROM (
SELECT
GROUP_CONCAT(city separator ',') AS all_city,
LENGTH(GROUP_CONCAT(citySEPARATOR ',')) - LENGTH(REPLACE(GROUP_CONCAT(citySEPARATOR ','), ',', '')) + 1 AS count_city
FROM table_name
) t
JOIN numbers n
ON n.num <= t.count_city
GROUP BY one_city
ORDER BY cnt DESC;
for getting count of comma separated distinct value run above query but getting correct resulr you should use one more table **numbers** which have only one column num integer type and insert some values.
if you getting error during GROUP_CONCAT(city separator ',') AS all_city in this condition set a global variable " SET group_concat_max_len = 18446744073709547520; "
SELECT COUNT(*) AS jobs
FROM Jobs
WHERE FIELD_IN_SET('New York') > 0
;
You should read about database normalization though. Having a comma separated list of values in a database table always has a 'smell', e.g. you can only check for a specific city name here and can't easily create a list of job counts for all cities referred to in the job table in one go ...
See e.g. http://en.wikipedia.org/wiki/Database_normalization for a start ...
Make JobId and City column as joined primary key. This will make your life easier. Do not insert multiple cities separated by commas.
-------------------------------------------------
JobId City // Other columns
-------------------------------------------------
1 New York
2 New York
2 Ohio
2 Virginia
3 New York
3 Virginia
Now you make the query will be something like this
select city, count(jobid) FROM TABLE GROUP BY city // Result will be New York 3, Ohio 1 and Virginia 2
Design your table as mentioned below. Each city name and Job Id in each row.
Job ID City Name
1 New York
2 New York
1 Seattle
Then use the query as mentioned in the below link.
SQL: How to get the count of each distinct value in a column?
Try this query,
SELECT COUNT(jobid), city FROM TABLE where city like '%newyork%';