SQL Query filtering advice - mysql

I'm not sure what I'm trying to do is possible but I've been trying to get different methods to a solution I need but so far I've come up empty handed.
Lets say I have 2 tables (just an example, in my case theres a hell of a lot more + alot more data)
One called clients and the other called form_data.
We have multiple clients in the clients table and in the form_data table we have multiple rows for each company present in the clients table. In form_data we store the serialized data from different forms. (id and data)
I'm currently pulling all records from the form_data table and I am trying to use a regexp on the data column to filter for instance that the value 'motor oil' is found in them.
I would like a way to do this filter but filter the company and not the forms .. so I want to find the forms that have 'motor oil' in them and the remove all entries for COMPANIES that don't have this match, but I want to keep all the forms showing for the companies that match.
I can post my query but it is rather long and i think if we can solve the above it should be sufficient for me to implement into the actual query.
Regards
EDIT:
SELECT f.form_question_has_answer_id AS f__form_question_has_answer_id, f.form_question_has_answer_request AS f__form_question_has_answer_request,
f.form_question_has_answer_form_id AS f__form_question_has_answer_form_id, f.form_question_has_answer_user_id AS f__form_question_has_answer_user_id,
p.project_company_has_user_id AS p__project_company_has_user_id, p.project_company_has_user_project_id AS p__project_company_has_user_project_id,
p.project_company_has_user_user_id AS p__project_company_has_user_user_id, c.company_id AS c__company_id, c.company_hall_no AS c__company_hall_no,
c.company_type AS c__company_type, c.company_company_name AS c__company_company_name, c.company_country AS c__company_country,
c.company_stand_number AS c__company_stand_number, c.company_image_file_1 AS c__company_image_file_1, p2.project_id AS p2__project_id,
p2.project_name AS p2__project_name, u.user_id AS u__user_id, u.user_username AS u__user_username, f2.form_id AS f2__form_id
FROM form_question_has_answer f
INNER JOIN project_company_has_user p ON f.form_question_has_answer_user_id = p.project_company_has_user_user_id
INNER JOIN company c ON p.project_company_has_user_company_id = c.company_id
INNER JOIN project p2 ON p.project_company_has_user_project_id = p2.project_id
INNER JOIN user u ON p.project_company_has_user_user_id = u.user_id
INNER JOIN form f2 ON p.project_company_has_user_project_id = f2.form_project_id
WHERE f.form_question_has_answer_id IN ('19262', '21560', '23088', '22660', '14772', '18495', '18720', '21625', '19957', '20943')
AND ((f2.form_template_name = "custom" AND p.project_company_has_user_garbage_collection = 0 AND p.project_company_has_user_project_id = 29) AND f.form_question_has_answer_request REGEXP 'item-cadcae')
ORDER BY company_company_name asc
The query is from a doctrine query.
EDIT:
If i have a company with 10 forms in the form_data table if i filter by 'motor oil' all forms for that company that don't have motor oil are removed.. so if only 1 of the 10 forms for company id 144 has the word motor oil then the other 9 forms are lost in the query.. I want to keep them. What I want is any company that didn't find any forms with that match (motor oil) to have all their forms removed from the search.

All form data for all customers who have at least one form that matches the search:
SELECT * FROM `form_data`
WHERE `clientid` IN (
SELECT DISTINCT `clientid` FROM `form_data`
WHERE `data` RLIKE '[[:<:]]motor oil[[:>:]]'
);

SELECT
c.*
FROM
clients as c
INNER JOIN (
select
*
from
forms
where
sometext like '%motor oil%'
) as f
ON f.client = c.id
http://sqlfiddle.com/#!3/5b616/1
in this part:
select
*
from
forms
where
sometext like '%motor oil%'
you can put any query that selects the relevant rows from your forms table with the appropriate filters.
you can do the inverse too with a left outer join where null technique.. though in this case modifying the subquery to return a list of distinct clients will probably give you more like the results you expect:
SELECT
c.*
FROM
clients as c
LEFT OUTER JOIN (
select
distinct
client
from
forms
where
sometext like '%motor oil%'
) as f
ON f.client = c.id
where f.client is null
http://sqlfiddle.com/#!3/5b616/4
so with your query something like:
SELECT
c.*
FROM
company AS c
INNER JOIN (
SELECT
DISTINCT c.company_id
FROM
form_question_has_answer f
INNER JOIN project_company_has_user p
ON f.form_question_has_answer_user_id = p.project_company_has_user_user_id
INNER JOIN company c
ON p.project_company_has_user_company_id = c.company_id
INNER JOIN project p2
ON p.project_company_has_user_project_id = p2.project_id
INNER JOIN user u
ON p.project_company_has_user_user_id = u.user_id
INNER JOIN form f2
ON p.project_company_has_user_project_id = f2.form_project_id
WHERE
f.form_question_has_answer_id IN ('19262', '21560', '23088', '22660', '14772', '18495', '18720', '21625', '19957', '20943')
AND ((f2.form_template_name = "custom"
AND p.project_company_has_user_garbage_collection = 0
AND p.project_company_has_user_project_id = 29)
AND f.form_question_has_answer_request REGEXP 'item-cadcae')
ORDER BY company_company_name asc
) as f
ON f.company_id= c.id

Not sure What do you mean by keeping all the forms showing for the companies that match?
Aren't just getting at this...
select * from clients inner join form_data on clients.id = form_data.id where form_data.[data] like '%motor oil%'

This will return all forms with 'motor oil' in them filled for the companies which also have 'motor oil' in their name:
SELECT *
FROM clients c
JOIN form_data fd
ON fd.client = c.id
WHERE fd.data RLIKE '[[:<:]]motor oil[[:>:]]'
AND c.name RLIKE '[[:<:]]motor oil[[:>:]]'

Related

Selecting multiple rows in same table in the same JOIN

In the picture is my table situation right now:
The central table in this case right now is tblJob, here is everything defined what I need (not all in the picture).
The address table needs to return 2 values (1 of the company and 1 of the job itself). The only thing I need to do right now is to add the company address (the job address is already in my query) My query already looks like this:
SELECT
tblJob.jobID,
tblJob.amount AS jobAmount,
tblJob.extraInfo AS jobExtraInfo,
tblJob.views AS jobViews,
tblJob.description AS jobDescription,
tblJob.dateCreated AS jobDateCreated,
tblJobFunction.jobFunctionID,
tblJobFunction.jobFunction,
tblAddress.zipcode AS jobAddress,
tblAddress.city AS jobCity,
tblAddress.street AS jobStreet,
tblAddress.number AS jobNumber,
tblAddress.bus AS jobBus,
tblCountry.countryID AS jobCountryID,
tblCountry.country AS jobCountry,
tblCountry.areaCode AS jobAreaCode,
tblCompany.companyID,
tblCompany.name,
tblCompany.email,
tblCompany.GSM,
tblCompany.phoneNumber,
tblCompany.photoURL AS companyPhotoURL,
tblCompany.VATNumber,
tblCompany.websiteURL,
tblEvent.eventID,
tblEvent.event,
tblEvent.description AS eventDescription,
tblEvent.startDate AS eventStartDate,
tblEvent.endDate AS eventEndDate,
tblEvent.facebookURL,
tblEvent.photoURL AS eventPhotoURL,
tblEvent.views AS eventViews,
tblEvent.dateCreated AS eventDateCreated
FROM tblJob
JOIN tblAddress ON tblAddress.addressID = tblJob.addressID
JOIN tblCountry ON tblAddress.countryID = tblCountry.countryID
JOIN tblJobFunction ON tblJob.jobFunctionID =
tblJobFunction.jobFunctionID
JOIN tblCompany ON tblJob.companyID = tblCompany.companyID
LEFT JOIN tblEvent ON tblJob.eventID = tblEvent.eventID
Now the question is: how can I add the address from the company in the same query?
Use the address table as many times as you need it, but each time you must give it a new alias:
FROM tblJob
JOIN tblAddress ON tblAddress.addressID = tblJob.addressID
JOIN tblCountry ON tblAddress.countryID = tblCountry.countryID
JOIN tblJobFunction ON tblJob.jobFunctionID = tblJobFunction.jobFunctionID
JOIN tblCompany ON tblJob.companyID = tblCompany.companyID
JOIN tblAddress a2 ON a2.addressID = tblCompany.addressID
LEFT JOIN tblEvent ON tblJob.eventID = tblEvent.eventID
perhaps more like this:
SELECT JobAddress.street, CompanyAddress.street
FROM tblJob
JOIN tblAddress JobAddress ON JobAddress.addressID = tblJob.addressID
JOIN tblCompany ON tblJob.companyID = tblCompany.companyID
JOIN tblAddress CompanyAddress ON CompanyAddress.addressID = tblCompany.addressID

MySQL, select, comparing values from 2 tables

in my database I want to select every construction_manual
where storage_room.quantity > component.quantity
But when I use:
SELECT construction_manual.name
FROM construction_manual cm, construction_manual_component cmc, component, storage_room
WHERE cm.ID = cmc.construction_manual_ID
AND cmc.component_ID = component.ID
AND component.storage_room_ID = storage_room.ID
AND storage_room.quantity > component.quantity
It will select every construction_manual.name as long as the first component in the storage_room has enough quantity.
So let's say...
construction_manual.1 needs
component.A -> quantity 5
component.B -> quantity 10
and in the storage room there is:
component.A -> quantity 6
component.B -> quantity 0
Although there is not enough in the storage room, construction_manual.1 will be selected. How do I select only those construction_manuals where there are enough components in the storage_room?
edit:
In my 4 tables are the following datasets. I will get the following result when I use the query mentioned above:database . But I should not be able to construct a table because there are not enough wooden_plates (wooden planks and not wooden plates ofc...ups)
I think what you want to ask is to ignore or exclude any construction manual that does not have enough components to satisfy a build. If that's the case you can do something like the below (a left join would also do it):
select distinct cm.name as construction_manual
from construction_manual cm
join construction_manual_component cmc
on cmc.construction_manual_ID = cm.ID
join component c
on c.ID = cmc.component_ID
join storage_room sr
on sr.ID = c.storage_room_ID
where sr.quantity > c.quantity
and cm.ID not in (select cm.ID
from construction_manual cm
join construction_manual_component cmc
on cmc.construction_manual_ID = cm.ID
join component c
on c.ID = cmc.component_ID
join storage_room sr
on sr.ID = c.storage_room_ID
where sr.quantity < c.quantity);

Use inner join, left join or right join for join 2 tables

I'm realizing a project which raises me the next tables:
For being more specific: First table:
'docs' => doc_id, doc_type_type_id, clients_cli_id
where doc_type_type_id
invoices
reference guides
Second Table:
'client' => cli_id
What I try to do is to join Client with doc that My query is:
Show Client with his invoice and reference guide:
SELECT c.cli_name, d1.doc_file as f1 , d2.doc_file as f2 FROM clients c INNER JOIN docs d1 ON d1.client_cli_id = c.cli_id INNER JOIN docs d2 ON d2.client_cli_id = c.cli_id WHERE d1.doc_fec=d2.doc_fec
select * from docs
inner join client on docs.clients_cli_id = client.cli_id
where doc_type_type_id = 1
Something in this format should give you all invoices joined to client.

MySQL view with 5 table join is not returning any rows

Here is my view creation.. Which is not returning any rows..
CREATE VIEW TOP AS
(
SELECT
a.USER_ID, a.USER_NAME, a.PASSWORD, a.FIRST_NAME, a.LAST_NAME, a.CONTACT_NUMBER, a.EMERGENCY_NUMBER, a.CITIZEN_SHIP, a.VISA_TYPE,
b.PLACE_CODE, b.PLACE_NAME, b.AIR_AVAILIBILITY, b.TRAIN_AVAILIBILITY, b.ROAD_AVAILIBILITY,
c.CLIENT_ID, c.CLIENT_NAME, c.CLIENT_ADDRESS, c.CLIENT_LOCATION,
d.PROJECT_CODE, d.PROJECT_NAME,
e.REQUEST_ID, e.REQUEST_STATUS, e.REQUEST_FOR, e.REQUEST_MODE, e.REQUEST_TYPE, e.TRAVEL_FROM, e.TRAVEL_TO, e.TRAVEL_DATE, e.TRAVEL_TICKET_BY, e.ASSIGNMENT_PLACE,
e.ASSIGNMENT_COUNTRY, e.TRAVEL_PURPOSE, e.ASSIGNMENT_DURATION, e.ASSIGNMENT_START_DATE, e.ASSIGNMENT_ACCOMODATION_BY, e.ACCOMODATION_BILLABLE, e.TRAVEL_BILLABLE, e.ASSIGNMENT_BILLABLE, e.NSHORE_PROJECT_BILLABLE
FROM USERS a, PLACES b, CLIENTS c, PROJECTS d, REQUESTS e
WHERE
a.USER_ID = e.USER_ID
AND b.PLACE_CODE = e.TRAVEL_FROM
AND b.PLACE_CODE = e.TRAVEL_TO
AND c.CLIENT_ID = d.CLIENT_ID
AND d.PROJECT_CODE = e.PROJECT_CODE
);
I think the problem is in these two lines..
AND b.PLACE_CODE = e.TRAVEL_FROM
AND b.PLACE_CODE = e.TRAVEL_TO
where I'm trying to reference to the same table more than once. Sorry if I'm asking really a very basic question. Im completely new to databases.
Looks like you are trying to get separate details for the place TRAVEL_FROM and the place TRAVEL_TO. To do that, you need to join twice against the same table with different aliases. I'm going to replace your implicit (comma-separated FROM) joins with explicit INNER JOINs which are preferred:
CREATE VIEW TOP AS
(
SELECT
a.USER_ID, a.USER_NAME, a.PASSWORD, a.FIRST_NAME, a.LAST_NAME, a.CONTACT_NUMBER, a.EMERGENCY_NUMBER, a.CITIZEN_SHIP, a.VISA_TYPE,
/* one set of columns for TRAVEL_FROM, each with its own alias */
b_from.PLACE_CODE AS PLACE_CODE_from, b_from.PLACE_NAME AS PLACE_NAME_from, b_from.AIR_AVAILIBILITY AS AIR_AVAILABILITY_from, b_from.TRAIN_AVAILIBILITY AS TRAIN_AVAILABILITY_from, b_from.ROAD_AVAILIBILITY AS ROAD_AVAILABILITY_from,
/* and one set for TRAVEL_TO, each with its own alias*/
b_to.PLACE_CODE AS PLACE_CODE_to, b_to.PLACE_NAME AS PLACE_NAME_to, b_to.AIR_AVAILIBILITY AS AIR_AVAILABILITY_to, b_to.TRAIN_AVAILIBILITY AS TRAIN_AVAILABILITY_to, b_to.ROAD_AVAILIBILITY AS ROAD_AVAILABILITY_to,
c.CLIENT_ID, c.CLIENT_NAME, c.CLIENT_ADDRESS, c.CLIENT_LOCATION,
d.PROJECT_CODE, d.PROJECT_NAME,
e.REQUEST_ID, e.REQUEST_STATUS, e.REQUEST_FOR, e.REQUEST_MODE, e.REQUEST_TYPE, e.TRAVEL_FROM, e.TRAVEL_TO, e.TRAVEL_DATE, e.TRAVEL_TICKET_BY, e.ASSIGNMENT_PLACE,
e.ASSIGNMENT_COUNTRY, e.TRAVEL_PURPOSE, e.ASSIGNMENT_DURATION, e.ASSIGNMENT_START_DATE, e.ASSIGNMENT_ACCOMODATION_BY, e.ACCOMODATION_BILLABLE, e.TRAVEL_BILLABLE, e.ASSIGNMENT_BILLABLE, e.NSHORE_PROJECT_BILLABLE
FROM
/* implicit joins replaced with preferred explicit joins */
USERS a
INNER JOIN REQUESTS e ON a.USER_ID = e.USER_ID
/* Join frist against PLACES for TRAVEL_FROM */
INNER JOIN PLACES b_from ON b_from.PLACE_CODE = e.TRAVEL_FROM
/* And again against PLACES for TRAVEL_TO */
INNER JOIN PLACES b_to ON b_to.PLACE_CODE = e.TRAVEL_TO
INNER JOIN PROJECTS d ON d.PROJECT_CODE = e.PROJECT_CODE
INNER JOIN CLIENTS c ON c.CLIENT_ID = d.CLIENT_ID
);

SQL: Get latest entries from history table

I have 3 tables
person (id, name)
area (id, number)
history (id, person_id, area_id, type, datetime)
In this tables I store the info which person had which area at a specific time. It is like a salesman travels in an area for a while and then he gets another area. He can also have multiple areas at a time.
history type = 'I' for CheckIn or 'O' for Checkout.
Example:
id person_id area_id type datetime
1 2 5 'O' '2011-12-01'
2 2 5 'I' '2011-12-31'
A person started traveling in area 5 at 2011-12-01 and gave it back on 2011-12-31.
Now I want to have a list of all the areas all persons have right now.
person1.name, area1.number, area2.number, area6.name
person2.name, area5.number, area9.number
....
The output could be like this too (it doesn't matter):
person1.name, area1.number
person1.name, area2.number
person1.name, area6.number
person2.name, area5.number
....
How can I do that?
This question is, indeed, quite tricky. You need a list of the entries in history where, for a given user and area, there is an 'O' record with no subsequent 'I' record. Working with just the history table, that translates to:
SELECT ho.person_id, ho.area_id, ho.type, MAX(ho.datetime)
FROM History AS ho
WHERE ho.type = 'O'
AND NOT EXISTS(SELECT *
FROM History AS hi
WHERE hi.person_id = ho.person_id
AND hi.area_id = ho.area_id
AND hi.type = 'I'
AND hi.datetime > ho.datetime
)
GROUP BY ho.person_id, ho.area_id, ho.type;
Then, since you're really only after the person's name and the area's number (though why the area number can't be the same as its ID I am not sure), you need to adapt slightly, joining with the extra two tables:
SELECT p.name, a.number
FROM History AS ho
JOIN Person AS p ON ho.person_id = p.id
JOIN Area AS a ON ho.area_id = a.id
WHERE ho.type = 'O'
AND NOT EXISTS(SELECT *
FROM History AS hi
WHERE hi.person_id = ho.person_id
AND hi.area_id = ho.area_id
AND hi.type = 'I'
AND hi.datetime > ho.datetime
);
The NOT EXISTS clause is a correlated sub-query; that tends to be inefficient. You might be able to recast it as a LEFT OUTER JOIN with appropriate join and filter conditions:
SELECT p.name, a.number
FROM History AS ho
JOIN Person AS p ON ho.person_id = p.id
JOIN Area AS a ON ho.area_id = a.id
LEFT OUTER JOIN History AS hi
ON hi.person_id = ho.person_id
AND hi.area_id = ho.area_id
AND hi.type = 'I'
AND hi.datetime > ho.datetime
WHERE ho.type = 'O'
AND hi.person_id IS NULL;
All SQL unverified.
You're looking for results where each row may have a different number of columns? I think you may want to look into GROUP_CONCAT()
SELECT p.`id`, GROUP_CONCAT(a.`number`, ',') AS `areas` FROM `person` a LEFT JOIN `history` h ON h.`person_id` = p.`id` LEFT JOIN `area` a ON a.`id` = h.`area_id`
I haven't tested this query, but I have used group concat in similar ways before. Naturally, you will want to tailor this to fit your needs. Of course, group concat will return a string so it will require post processing to use the data.
EDIT I thikn your question has been edited since I began responding. My query does not really fit your request anymore...
Try this:
select *
from person p
inner join history h on h.person_id = p.id
left outer join history h2 on h2.person_id = p.id and h2.area_id = h.area_id and h2.type = 'O'
inner join areas on a.id = h.area_id
where h2.person_id is null and h.type = 'I'