SQL Genius need .. Complex MySQL query - mysql

I am trying to optimise my php by doing as much work on the MySQL server as possible. I have this sql query which is pulling data out of a leads table, but at the same time joining two tags tables to combine the result. I am looking to add a company which is linked through a relations table.
So the table that holds the relationship between the two is relations_value which simply states (I add example data)
parenttable (companies) | parentrecordid (10) | childtable (leads) | childrecordid (1)
the companies table has quite a few columns but the only two relevant are;
id (10) | companyname (my company name)
So this query currently grabs everything I need but I want to bring the companyname into the query:
SELECT leads.id,
GROUP_CONCAT(c.tag ORDER BY c.tag) AS tags,
leads.status,
leads.probability
FROM `gs_db_1002`.leads
LEFT JOIN ( SELECT *
FROM tags_module
WHERE tagid IN ( SELECT id
FROM tags
WHERE moduleid = 'leads' ) ) as b
ON leads.id = b.recordid
LEFT JOIN `gs_db_1002`.tags as c
ON b.tagid = c.id
GROUP BY leads.id,
leads.status,
leads.probability
I need to be able to go into the relations_values table and pull parenttable and parentrecordid by selecting childtable = leads and childrecordid = 1 and somehow join these so that I am able to get companyname as a column in the above query...
Is this possible?
I have created a sqlfiddle: sqlfiddle.com/#!2/023fa/2 So I am looking to add companies.companyname as column to the query.

I don't know what your primary keys and foreign keys are that link each table together.. if you could give a better understanding of what ID's are linked to eachother it would make this a lot easier... however i did something that does return the correct result... but since all of the ID's are = 1 then it could be incorrect.
SELECT
leads.id, GROUP_CONCAT(c.tag ORDER BY c.tag) AS tags,
leads.status, leads.probability, companyname
FROM leads
LEFT JOIN (
SELECT * FROM tags_module WHERE tagid IN (
SELECT id FROM tags WHERE moduleid = 'leads' )
) as b ON leads.id = b.recordid
LEFT JOIN tags as c ON b.tagid = c.id
LEFT JOIN relations_values rv on rv.id = b.recordid
LEFT JOIN companies c1 on c1.createdby = rv.parentrecordid
GROUP BY leads.id,leads.status, leads.probability

Related

How to Select data trough 2 tables with ONE Query

Every client has their own project but not all of them has project.
I want to select all clients that has project but I do not know how to do it!
I get all clients with this query:
SELECT `clients` FROM `reps` WHERE `clients` != ''
My goal is to get data which has only project
These are my database tables:
Table Clients: (Table name = reps)
1 id varchar(12) // example: stckvrflw
2 ctitle varchar(100) // example: StackOverflow
Table Projects: (Table name = verkocht)
1 id varchar(11) // example: 1
2 title varchar(100) // example: This is an Example
Do you have a solution on my problem?
You have the client in the milestones table, so doesn't this do what you want?
SELECT DISTINCT m.client
FROM `milestones` m;
You have to join the projects and the milestones tables. Since you gave no information on your database shema, the field names have to be adjusted. You also have to check for deleted or hidden flags if you have some:
SELECT c.* FROM clients c
INNER JOIN projects p ON p.client = c.id
INNER JOIN milestones m ON m.project = p.id
GROUP BY c.id

How to select column from 2nd level depth subquery?

I have found a few answers how to select a column from subquery, but it doesn't seem to be working if there is a subquery inside subquery.
My table relationships are as follow (simplified, with example):
Plans
| id | role_id
| --------------
| 4 | 2
Roles
| id | name
----------------
| 2 | Operator
Assignments
| role_name | user_id
| ---------------------
| Operator | 12
Table Plans have foreign key role_id to table Roles column id. And Table Roles have foreign key id to table Assignments column role_name.
I'm writing a query that would select Plans but instead of role_id I would show user_id from table Assignments.
I'm pretty close with my current work but I'm stuck at final step:
SELECT assigned_user, plan.id, plan.role_id
FROM `Plans` AS plan
LEFT JOIN (
SELECT roleTable.id, roleTable.name
FROM `Roles` AS roleTable
INNER JOIN (
SELECT base_assign.user_id AS assigned_user, base_assign.role_name
FROM `Assignments` AS base_assign) AS roleAssign
ON roleTable.name = roleAssign.role_name
) AS role
ON (plan.assignee_role = role.id)
Column assigned_user on SELECT part (line 1) is not found but I tried by adding alias inside of subquery SELECT. How I can select base_assign.user_id? Note that I (most likely) cannot modify database structure as it was given like this in the first place.
You are creating subqueries too excessively and you don't even need subqueries for this. Subqueries can be materialized by optimzer in MySQL which can cause performance issues.
See https://dev.mysql.com/doc/refman/5.6/en/subquery-optimization.html
Try this using simple left joins:
select a.user_id as assigned_user,
p.id,
p.role_id
from `Plans` p
left join `Roles` r on p.assignee_role = r.id
left join `Assignments` a on r.name = a.role_name;
You query wasn't working because you were not selecting the column while joining.
Your somewhat "fixed" code:
select assigned_user,
plan.id,
plan.role_id
from `Plans` as plan
left join (
select roleAssign.assigned_user roleTable.id,
roleTable.name
from `Roles` as roleTable
inner join (
select base_assign.user_id as assigned_user,
base_assign.role_name
from `Assignments` as base_assign
) as roleAssign on roleTable.name = roleAssign.role_name
) as role on (plan.assignee_role = role.id)
You don't need subqueries for this. It is a little confusing what you are trying to do. The following will return all plans, even those with no roles and no assignments:
SELECT b.user_id as assigned_user, p.id, p.role_id
FROM Plans p LEFT JOIN
Roles r
ON p.role_id = r.id LEFT JOIN
Assignments a
ON a.name = r.name;
Subqueries are bad for two reasons. In MySQL, the are materialized. That means extra overhead for reading and writing the tables. Plus, you lose any indexes on them.
In your case, the subqueries just add layer upon layer of naming, and the names really do not help make your query more understandable.

Change database structure: need an SQL query

I have two tables:
connections
id | publisherId | authorId
and
books
id | connectionId | title
I want to merge these tables to get only one table:
books
id| publisherId | authorId | title
How can I do this with only one SQL query?
CREATE TABLE newtable
SELECT b.id, c.publisherId, c.authorID, b.title
FROM books b
INNER JOIN connections c
on c.id = b.connectionId
Untested, but this should do it. I assume you want the ID from the books table, otherwise you need c.id instead of b.id.
Then you can drop the old tables and rename this to whatever you want.
CREATE TABLE connections_books
SELECT books.id as id,
connections.publisherId as publisherId,
connections.authorId as authorId,
books.title as title
FROM books join connections on books.connectionId = connections.id;
Test the query first using just the select part:
SELECT books.id as id,
connections.publisherId as publisherId,
connections.authorId as authorId,
books.title as title
FROM books join connections on books.connectionId = connections.id;
Once this gives you what you want, go ahead and create the table.
It's important to make sure the column names in the new table are correct. Use the select statement testing to ensure your column names are what you want. If you want to change the column names, change the as ... names for each column selected.

sql - join 2 tables 10 times

I have 2 tables structured like this:
People
----------
>ID, Name, Surname
Projects
----------
>ID, Assistant#1, Assistant#2, Assistant#3, Assistant#4,
> Assistant#5, Assistant#6, Assistant#7, Assistant#8, Assistant#9,
> Assistant#10, lot of other columns..... (every assistant column
> contains an integer, the people ID)
Now, I want to query my DB and get a single row from Project table (let's suppose row with ID=3) containing all columns of project table and in particular have each assistant replaced with it's correct name and surname.
I've managed to do this (with a lot of joins), but with my little sql experience my code looks very bulky and i'm sure that there's a better way to do this.
Thanks in advance.
SELECT p.Name, p.Surname
FROM People p
CROSS JOIN Project j ON (p.PeopleID = j.Assistant1 OR
p.PeopleID = j.Assistant2 OR
p.PeopleID = j.Assistant3 OR
.. etc)
AND j.ProjectID = 3
Your performance will be awful, but that's the price you pay for having a horribly designed database. The better solution is to map a project and people by the means of a decomposition:
CREATE TABLE proj_people
People_ID INT
Project_ID INT
Your structure is not clear to me. I advice you to create a clutch for projects.
Tables:
Peoples
people_id, people_name, people_surname
Projects
project_id, project_name
Clutch
project_id, people_id
Because you set a number of (possible) assistants at 10, that dictates that you are going to have to write code to accomodate those 10 assistants. This can be done a few ways: (have not tested)
select * from projects proj
left join people p1 on proj.assistant1 = p1.peopleid
left join people p2 on proj.assistant2 = p2.peopleid
left join people p3 on proj.assistant3 = p3.peopleid
left join people p4 on proj.assistant4 = p4.peopleid
left join people p5 on proj.assistant5 = p5.peopleid
left join people p6 on proj.assistant6 = p6.peopleid
left join people p7 on proj.assistant7 = p7.peopleid
left join people p8 on proj.assistant8 = p8.peopleid
left join people p9 on proj.assistant9 = p9.peopleid
left join people p10 on proj.assistant10 = p10.peopleid
otherwise you could do some trickery with
select proj.projectID,
(select * from people where peopleID = proj.assistant1),
(select * from people where peopleID = proj.assistant2),
(select * from people where peopleID = proj.assistant3),
(select * from people where peopleID = proj.assistant4),
(select * from people where peopleID = proj.assistant5),
(select * from people where peopleID = proj.assistant6),
(select * from people where peopleID = proj.assistant7),
(select * from people where peopleID = proj.assistant8),
(select * from people where peopleID = proj.assistant9),
(select * from people where peopleID = proj.assistant10)
from projects proj
If possible, it might be best to re-structure your datatables, and have a single assistant mapped to a single projectID:
PeopleID, Name, Surname
ProjectID, PeopleID
Therefore, you could do just one inner join and you will return a row for each assistant:
select * from projects proj
inner join people p
on p.peopleID = proj.peopleid
where proj.projectID = PROJECTID
Your problem is that you are trying to solve a data presentation issue at the database level and this is rarely a good idea. You should have a table holding projectid and assistantid only, having the combination of both columns as the primary key of the table. This will allow you to join this table to the assistant table and the project table only once and get all the results you need. This will also have the benefit of allowing you to support many more assistants on a project in the future without having to alter your queries to add yet more joins.
In conclusion you should have 3 tables:
Project
Assistant
ProjectAssistant (projectid,assistantid)
you want to restructure your tables. I hate seeing tables with multiple columns for essentially the same thing multiple times. If you restructure to have 3 tables:
Person
PeopleID | Name | Surname
Project
ProjectID | ...other columns except Assistant
Project_Person
ProjectID | PersonID
That way you can assign multiple assistants to the same project without the need for multiple Assistant columns. Then you could use something like this to get the required results:
SELECT proj.ProjectID, pers.Name, pers.Surname
FROM Person pers
INNER JOIN Project_Person pp ON pp.PersonID = pers.PersonID
INNER JOIN Project proj ON proj.ProjectID = pp.ProjectID
This will not return a single row, but that is not how SQL is designed to work.
If you don't want to/can't change the table structure then maybe use a user defined function?
CREATE FUNCTION GET_ASS_NAME (n1 ASS_NO)
RETURNS VARCHAR(50)
DETERMINISTIC
BEGIN
DECLARE fullname VARCHAR(50);
SELECT fullname = CONCAT(NAME, ' ', SURNAME) FROM PEOPLE WHERE ID = n1;
RETURN fullname;
END|
then
select GET_ASS_NAME(1), GET_ASS_NAME(2), GET_ASS_NAME(3), ...
from PROJECTS
where ID = 3
Perhaps a cleaner design is:
People table:
People ID, Name, Surname
ProjectAssistants table:
Project ID, Assistant Person ID
Projects table:
Project ID, lots of other columns (but no Assistant columns)
Then, the SQL becomes something like:
SELECT pr.*, p.*
FROM Projects pr
JOIN ProjectAssistants p_a ON pr.ProjectID = p_a ProjectID
JOIN People p ON p_a.AssistantPersonID = p.PeopleID
WHERE ProjectID = X
Not tested :)
Select * from project pr
left join people pe1 on pe1.peopleid = pr.astint1
left join people pe2 on pe2.peopleid = pr.astint2
left join people pe3 on pe3.peopleid = pr.astint3
left join people pe4 on pe4.peopleid = pr.astint4
left join people pe5 on pe5.peopleid = pr.astint5
left join people pe6 on pe6.peopleid = pr.astint6
left join people pe7 on pe7.peopleid = pr.astint7
left join people pe8 on pe8.peopleid = pr.astint8
left join people pe9 on pe9.peopleid = pr.astint9
left join people pe10 on pe10.peopleid = pr.astint10
where pr.projectid = 3

MySQL join to pivot question

I'm sure this is simple but my brain just isnt working today!
I have a table products, lets just assume it contains p_id, i have another table - a pivot table - which references between products and another table attributes, this table is products_to_attributes and contains pta_aid (attribute id) and pta_pid (product id)
Hopefully this (incorrect) query will show what i want to do better than i can explain it:
SELECT `p_id` FROM `products`
LEFT JOIN `products_to_attributes` ON (`pta_pid` = `p_id`)
WHERE ((`pta_aid` = '1' OR `pta_aid` = '2') AND(`pta_aid` = '3'))
I want to be able to group together attributes where a product must have attribute 1 Or attribute 2 AND have attribute 3.
If I understand your need correctly, you pretty much have to have two EXISTS clauses:
SELECT p_id FROM products PR
WHERE EXISTS (SELECT p_id FROM products_to_attributes WHERE (pta_aid = 1 OR pta_aid=2) AND pta_pid=PR.p_id )
AND EXISTS (SELECT p_id FROM products_to_attributes WHERE pta_aid = 3 AND pta_pid=PR.p_id)
SELECT p.`p_id` FROM `products` p
LEFT JOIN `products_to_attributes` pta ON (p.`p_id` = pta.`pta_pid`)
Name the tables and specify which field is from which table. However how a field can be both 1 and 3 in a row? there is a logical problem in your where clause.