Querying two tables with one query with replacing word - mysql

I have two tables in mySql like:
table 1: city
id name transport
1 new-york 1,3,4
2 dallas 3,4
3 la 1,2,4
4 california 3,4
table 2: transport
id name
1 bus
2 trolleybus
3 train
4 metro
Can I received result like example with one query?
result:
id name transport
1 new-york bus,train,metro
2 dallas train,metro
3 la bus,trolleybus,metro
4 california train,metro

You should change your database structure and normalize it. Never store data as comma-separation since its a bad way to store data. However till you fix the database design the following query should do what you are looking at.
select
id,
name,
group_concat(transport)
from
(
select
c.id,
c.name,
t.transport as transport
from city c
join transport t on find_in_set(t.id,c.transport)
)x
group by id ;
DEMO
If you need to order the transport values then you can use
group_concat(transport ORDER BY transport)
why is comma-separation is bad practice?
You can read the following why it should be ignored
Is storing a delimited list in a database column really that bad?
To normalize the database you will need to create another table as
city_transport (cid int , tid) ;
cid = city id
tid = transport id
For each city you will have multiple entry in this table. So the tables should look like
create table city (id int , name varchar(100));
insert into city values
(1,'new-york'),(2,'dallas'),(3,'la'),(4,'california');
create table transport (id int ,transport varchar(100));
insert into transport values
(1,'bus'),(2,'trolleybus'),(3,'train'),(4,'metro');
create table city_transport (cid int ,tid int);
insert into city_transport values
(1,1),(1,3),(1,4),(2,3),(2,4),(3,1),(3,2),(3,4),(4,3),(4,4);
And the query to get the same result is as
select
c.id,
c.name,
group_concat(t.transport order by t.transport) as transport
from city_transport ct
join city c on c.id = ct.cid
join transport t on t.id = ct.tid
group by c.id ;
When you have a large amount of data then essentially you will need index and then using join on indexed columns the performance will be way better than using find_in_set with comma separated list

You should work with a table between city and transport to be correct. That being said, you could fix this using REPLACE() and subqueries but the performance will be horrible.

Related

How to select column value from another database referenced by field inside same query?

suppose that i have the following table in my database called 'central':
id
Name
DB
customer
1
Bob
A1
1
2
Marley
NULL
NULL
3
Irene
A2
2
The customer column references to the 'Customers' table which is inside of another database.
IMPORTANT: the database names A1 and A2 are only examples. There will be hundreds of separate databases containing a Customers table.
The central.DB column is a reference to which database to use when selecting the CustomerName from inside of the select statement that you see below the following example databases.
An example from the Customers table inside of Database 'A1':
id
Customer
CustomerName
1
C001
Asta housing
2
C002
Jack's
An example from the Customers table inside of Database 'A2':
id
Customer
CustomerName
1
D900
Mo's
2
D901
Humpries paints
I can't figure out how to do something like this and maybe it's not possible at all:
SELECT Central.id, Central.Name, (central.DB).Customers.CustomerName
FROM central, (central.DB).Customers.
Or maybe use a join to do this?
Either way, the query result should should be:
id
Name
CustomerName
1
Bob
Asta housing
2
Marley
NULL
3
Irene
Humpries paints
Maybe this isn't possible and the result is that i must do it with php? But i'd rather not do this as central.DB could reference potentially hundreds of databases and that will possibly be rather expensive (a lot of calls to the referenced databases).
(I can't change the setup of using a central database and the other separate databases)
Is it possible to reference to another database(table) when the name of that database is inside of the result set?
If both databases are at the same server(MySQL Instance) you can just prefix the tables with the database name.
SELECT C.id, C.Name, CU.CustomerName FROM database_1.Central AS C LEFT JOIN database_2.Customers AS CU
Make sure that the user logged in has permission to access both databases.
SELECT t0.id,
t0.name,
COALESCE(t1.CustomerName, t2.CustomerName) CustomerName
FROM my_database.central t0
LEFT JOIN A1.Customers t1 ON (t0.customer, t0.DB) = (t1.ID, 'DB1')
LEFT JOIN A2.Customers t2 ON (t0.customer, t0.DB) = (t2.ID, 'DB2')

Inner join in mysql take a long time

I have table contacts with more than 1,000,000 and other table cities which have about 20,000 records. Need to fetch all cities which have used in contacts table.
Contacts table have following columns
Id, name, phone, email, city, state, country, postal, address, manager_Id
cities table have
Id, city
I used Inner join for this, but its taking a long time to go. Query takes more than 2 minutes to execute.
I used this query
SELECT cities.* FROM cities
INNER JOIN contacts ON contacts.City = cities.city
WHERE contacts.manager_Id= 1
created index on manager_Id as well. But still its very slow.
for better performance you could add index
on table cities column city
on table contacts a composite index on columns (manager_id, city)
Filter contacts first and then join to cities:
SELECT ct.*
FROM cities ct INNER JOIN (
SELECT city FROM contacts
WHERE manager_Id = 1
) cn ON cn.city = ct.city
You need indexes for city in both tables and for manager_id in contacts.
As others have pointed out about having proper index, I am taking it a bit more for clarification. You are specifically looking for contacts where the MANAGER ID = 1. This is not expected to be one person, but could be many people. So having the MANAGER ID in the first position will optimize get me all people for that manager. By having the city as part of the index via (manager_id, city), you are pulling the two data elements you need to optimize as part of the index. This way the engine does not have to go to the raw data pages to get the other part of interest.
Now, From that, you want all the city information (hence the join to city table on that ID).
Since you are only querying the CITIES and not the actual contact people information, you probably want to have DISTINCT City ID. Lets say a manager is responsible for 50 people and most of them live in the same city or neighboring. You may have 5 distinct cities? That too will limit your result set of joining.
Having said that, I would do a follows, and with MySQL, using STRAIGHT_JOIN can help optimize by "do the query as I wrote it, don't think for me".
select STRAIGHT_JOIN
cty.*
from
( select distinct c.City
from Contacts c
where c.Manager_ID = 1 ) PQ
JOIN Cities cty
on PQ.City = Cty.City
The "PQ" is an alias representing my "pre-query" of just DISTINCT cities for a given manager.
Again, have one index on Contacts table on (manager_id, city). On the city table, I would expect and index on (city).
You need two indexes, one on each table.
On the contacts table, first index manager_Id, then City
CREATE INDEX idx_contacts_mgr_city ON contacts(manager_Id, City);
On the cities table, just index `City.
Is the 'City' field from the table 'Contacts' a VARCHAR?
If that's the case, I see multiple things here.
First of all, since you have already have the 'Id' for the corresponding city in your 'cities' tables, I don't see why not to use the same 'Id' from the 'cities' table for the 'Contacts' table.
You can add the 'IdCity' field to the 'Contacts' table so you don't have to modify your existing records.
You'll have to insert the 'IdCity' manually though for each of your records, or you can create a Query using 'cities' table and then compare the 'idCity' but insert the 'city' (city name) in your 'Contacts' table.
Returning to your query:
Then, use an INT JOIN instead of a VARCHAR JOIN. Since you have many records, this can show up an important significance in performance.
It looks like you need to add two indexes, one on cities.city and one on (contacts.manager_Id, contacts.city). That should speed things up significantly.

sql table design to fetch records with multiple inclusion and exclusion conditions

We want to select customers based on following parameters i.e. customer should be in:
specific city i.e. cityId=1,2,3...
specific customerId should be excluded i.e. customerId=33,2323,34534...
specific age i.e. 5 years, 7 years, 72 years...
This inclusion & exclusion list can be any long.
How should we design database for this:
Create separate table 'customerInclusionCities' for these inclusion cities and do like:
select * from customers where cityId in (select cityId from customerInclusionCities)
Some we do for age, create table 'customerEligibleAge' with all entries of eligible age entries:
i.e. select * from customers where age in (select age from customerEligibleAge)
and Create separate table 'customerIdToBeExcluded' for excluding customers:
i.e. select * from customers where customerId not in (select customerId from customerIdToBeExcluded)
OR
Create One table with Category and Ids.
i.e. Category1 for cities, Category2 for CustomerIds to be excluded.
Which approach is better, creating one table for these parameters OR creating separate tables for each list i.e. age, customerId, city?
IN ( SELECT ... ) can be very slow. Do your query as a single SELECT without subqueries. I assume all 3 columns are in the same table? (If not, that adds complexity.) The WHERE clause will probably have 3 IN ( constants ) clauses:
SELECT ...
FROM tbl
WHERE cityId IN (1,2,3...)
AND customerId NOT IN (33,2323,34534...)
AND age IN (5, 7, 72)
Have (at least):
INDEX(cityId),
INDEX(age)
(Negated things are unlikely to be able to use an index.)
The query will use one of the indexes; having both will give the Optimizer a choice of which it thinks is better.
Or...
SELECT c.*
FROM customers AS c
JOIN cityEligible AS b ON b.city = c.city
JOIN customerEligibleAge AS ce ON c.age = ce.age
LEFT JOIN customerIdToBeExcluded AS ex ON c.customerId = ex.customerId
WHERE ex.customerId IS NULL
Suggested indexes (probably as PRIMARY KEY):
customers: (city)
customerEligibleAge: (age)
customerIdToBeExcluded: (customerId)
In order to discuss further, please provide SHOW CREATE TABLE for each table and EXPLAIN SELECT ... for any of the queries actually work.
If you use the database only that operation, I recommend to use the first solution. Also the first solution is very simple to deploy.
The second solution fills up with junk the DB.

custom made order by mysql

I have a row of entities stored in a mysql table;
entity table
order
id
name
type (refers to Type(id))
and a row of types stored in another table
Type table
id
name
order
the ORDER column in Type table specifies by what order the entities should be sorted - some should be ordered by name and some by id.
How do I create a mysql query that gets the ORDER BY clause from the type table and sorts the entities in the entity table by the ORDER stored for that entity type in the Type table
for example, let us say I have the following rows:
Entity table:
row 1:
id = 1
name = Virginia
type = 1
row 2:
id = 2
name = Virginia
type = 1
row 3:
id = 3
name = Canada
type = 2
types (rows in Type table)
row 1
id = 1
name = states
order = "name"
row 2:
id = 2
name = countries
order = id
I want to do the following query
SELECT entities.id, entities.name FROM entities INNER JOIN type ON entities.type = type.id ORDER BY ....
in the ORDER BY I want to order the entities based on what is stored in the ORDER row in the type table. So countries should be sorted by Entity(ID) and states should be sorted by Entity(name). How can I do that?
This doesn't seem like a very good design for a database. For your example, I would suggest something more similar to this:
CREATE TABLE countries (
countryID INT NOT NULL,
countryName VARCHAR(30)
);
CREATE TABLE states (
stateID INT NOT NULL,
countryID INT,
stateName VARCHAR(30)
);
Then you can perform queries like:
SELECT c.countryName, s.stateName
FROM countries c LEFT JOIN states s ON c.countryID = s.countryID
ORDER BY countryName, stateName;
At the very least, I would suggest using more obvious names for your columns, like in your entity table, you have a column named 'type' which refers to the 'id' field in the type table. Perhaps name them both typeID or something more obvious.
I also think it's a bad idea to create a column that stores information about which column to order by. Not only does that mean that you'll have to execute two queries every time (one to fetch the order by, and one to fetch the actual data), but you will also be storing a lot of extra data unnecessarily.

Cross table query

I have three tables.
course
idcourse
name
mark
idmark
idstudent
idcourse
mark
student
idstudent
name
How to cross a query to such a result?
result
table Header
studentid course1 course2 ... courseN
id1 mark1 mark2 ... markN
id2 mark1 mark2 ... markN
id3 mark1 mark2 ... markN
Best I can think if is to use the group_concat function.
Because a variable number of columns is just a maintenance nightmare.
You can strip the values from the coursegrades field apart by exploding the list in php or whatever comma separator you have in your client.
/*First make a list of coursenames for the header.*/
SELECT 'studentid' as idstudent
, 'studentname' as studentname
, GROUP_CONCAT(course.name ORDER BY couselist.idcourse) as coursegrades
FROM course
INNER JOIN (SELECT DISTINCT idcourse FROM mark) courselist
ON (courselist.idcourse = course.idcourse)
UNION ALL
/*UNION ALL this with a list of grades per course*/
SELECT idstudent
, student.name as studentname
, GROUP_CONCAT(IFNULL(mark.mark,'') ORDER BY courselist.idcourse) as coursegrades
FROM course
INNER JOIN (SELECT DISTINCT idcourse FROM mark) courselist
ON (courselist.idcourse = course.idcourse)
LEFT JOIN mark ON (mark.idcourse = courselist.idcourse)
INNER JOIN student ON (student.idstudent = mark.idstudent)
GROUP BY student
First you should have many-to-one foreign key relationships to insure that the idstudent field in the tables depending on the student table idstudent field (mark, course) match up.
To answer your specific question, it's not possible to return a result like this from the database client. Maybe if you wrote something as a procedure, but not with basic SQL. The best way to get the results you want is to do a simple query on the mark table to get the idstudent and mark fields and then manipulate them externally in another language.
So the query would be like this:
SELECT marks.idstudent, marks.mark, courses.name FROM marks,courses
WHERE marks.idcourse=courses.idcourse;
And you can display the results using an external program written in PHP or Python, for example.