I have 3 tables:
districts | id, zipcode, district
subscription | id, userid, status, level
profile | id, userid, zip
I am trying to count the amount of zip codes from active subscriptions and group them by district. ( I am using LEFT() so I can can include zips in the #####-#### format). The query works in 6.4 seconds locally, but on the server isn't outputting in a timely manner. What can I do to speed this up?
I have written:
SELECT COUNT( d.zipcode ) total, d.district
FROM districts AS d
JOIN profile AS p ON d.zipcode = LEFT(p.zip, 5)
JOIN subscriptions AS s ON s.userid = p.userid
WHERE s.status = 1
GROUP BY d.district
Thanks!
create an index on:
d.zipcode
p.zip
s.userid
p.userid
s.status
That's because you're using a function in your join. Therefore no index can be used. Why do you save zip in table profile? Save the districts.id in profile instead of zip, that would make sense.
EDIT: Actually you shouldn't do overindexing like Dweeves suggests. But you should add a foreign key to profile referencing districts.
ALTER TABLE profile ADD CONSTRAINT fk_zip_districts FOREIGN KEY (districtsId) REFERENCING districts(id);
assuming that you do like I said above.
profile | id, userId, districtsId
Related
Okay so this is my best attempt at making a query. It currently executes but returns nothing the other attempts have not worked.
The joins are wrong I believe and I'm not sure how to fix them or do this right
In this query I am trying to get the activity details with staff assigned to those activities matching all conditions below.
The staff and activities are linked in the Allocation table with a Staff ID assigned to an Activity ID there.
--Teach at least 2 modules during 2019 in a certain campus
The staffID must occur at least twice in the Teach table at the field StaffID
The spaID 5 or 6 would correspond to the campus so teach two modules at this time and ID.
--Supervise more than 1 colleagues
This is in the staff table with SupervisorID being a foreign key of StaffID in the same table.
So the Staff ID allocated to the activity needs to supervise more than one colleague with their ID appearing in SupervisorID more than once.
--The related activity is internal one
The activity is internal if it equals to 1.
So to sum it up the activity details should only appear in the query results if it has staff matching all the conditions above
SELECT Activity.AcID, Activity.Title, Activity.CaID, Activity.Internal, Activity.BuID, Budget.Amount FROM Activity
INNER JOIN Budget ON Activity.AcID = Budget.BuID
INNER JOIN Allocation ON Activity.AcID = Allocation.AcID
INNER JOIN Staff ON Allocation.StaffID = Staff.StaffID
INNER JOIN Teach ON Allocation.StaffID = Teach.StaffID
WHERE Activity.Internal=1 AND
Allocation.StaffID IN (
SELECT Staff.SupervisorID
FROM Staff
GROUP BY StaffID
HAVING COUNT(Staff.SupervisorID=Allocation.StaffID) >1)
AND Allocation.StaffID IN (
SELECT Teach.StaffID
FROM Teach
WHERE Teach.Year='2019' AND Teach.SpaID=5 OR 6
GROUP BY Teach.StaffID
HAVING COUNT(Allocation.StaffID=Teach.StaffID) >=2);
Table details are below if you want to look at them for more details
Table Activity -- AcID, Title, CaID, BuID, Status, Started, Ended Internal
Table Allocation -- StaffID, AcID
Table Budget -- BuID, Amount, Approver, Payee, Status
Table Campus -- CaID, Address, GmName, Country, Status
Table Classroom -- RmID, Capacity, CaID, Location, Type, Status
Table Module -- ModuleID, Module Name, DeptID, Programme, TMode, Date_of_Firstoffer
Table SpaceAssign -- SpaID, RmID, TID, Manager, Approved
Table Staff -- StaffID, Title, FirstName, LastName, DeptID, CaID, Joined, LeftD, Current, Salary, ContractType, SupervisorID
Table Teach -- TID, ModuleID, StaffID, SpaID, Semester, Year
I have tried my best to explain this well sorry for any confusion.
Without data It's very tough to find a solution. Please try this:
SELECT Activity.AcID, Activity.Title, Activity.CaID, Activity.Internal, Activity.BuID, Budget.Amount FROM Activity
INNER JOIN Budget ON Activity.AcID = Budget.BuID
INNER JOIN Allocation ON Activity.AcID = Allocation.AcID
INNER JOIN Staff ON Allocation.StaffID = Staff.StaffID
INNER JOIN Teach ON Allocation.StaffID = Teach.StaffID
WHERE Activity.Internal=1 AND
Allocation.StaffID IN (
SELECT Staff.SupervisorID
FROM Staff
GROUP BY SupervisorID
HAVING COUNT(*) >1)
AND Allocation.StaffID IN (
SELECT Teach.StaffID
FROM Teach
WHERE Teach.Year='2019' AND Teach.SpaID=5 OR 6
GROUP BY Teach.StaffID
HAVING COUNT(*) >=2);
It is hard to tell without having access to the data but you can try to change the inner joins to left joins and also:
This
WHERE Teach.Year='2019' AND Teach.SpaID=5 OR 6
Should be
WHERE Teach.Year='2019' AND (Teach.SpaID=5 OR Teach.SpaID=6)
This looks incorrect as well:
INNER JOIN Budget ON Activity.AcID = Budget.BuID
Should be:
INNER JOIN Budget ON Activity.BuID = Budget.BuID
I have a relation between users and groups. Users can be in a group or not.
EDIT : Added some stuff to the model to make it more convenient.
Let's say I have a rule to add users in a group considering it has a specific town, and a custom metadata like age 18).
Curently, I do that to know which users I have to add in the group of the people living in Paris who are 18:
SELECT user.id AS 'id'
FROM user
LEFT JOIN
(
SELECT user_id
FROM user_has_role_group
WHERE role_group_id = 1 -- Group for Paris
)
AS T1
ON user.id = T1.user_id
WHERE
(
user.town = 'Paris' AND JSON_EXTRACT('custom_metadata', '$.age') = 18
)
AND T1.user_id IS NULL
It works & gives me the IDs of the users to insert in group.
But when I have 50 groups to proceed, like for 50 town or various ages, it forces me to do 50 requests, it's very slow and not efficient for my Database.
How could I generate a result for each group ?
Something like :
role_group_id user_to_add
1 1
1 2
2 1
2 3
The only way I know to do that for now is to do an UNION on several sub queries like the one above, but of course it's very slow.
Note that the custom_metadata field is a user defined field. I can't create specific columns or tables.
Thanks a lot for your help.
if I good understood you:
select user.id, grp.id
from user, role_group grp
where (user.id, grp.id) not in (select user_id, role_group_id from user_has_role_group) and user.town in ('Paris', 'Warsav')
that code give list of users and group which they not belong from one of towns..
To add the missing entries to user_has_role_group, you might want to have some mapping between those town names and their group_id's.
The example below is just using a subquery with unions for that.
But you could replace that with a select from a table.
Maybe even from role_group, if those names correlate with the user town names.
insert into user_has_role_group (user_id, group_id)
select u.user_id, g.group_id
from user u
join (
select 'Paris' as name, 1 as group_id union all
select 'Rome', 2
-- add more towns here
) g on (u.town = g.name)
left join user_has_role_group ug
on (ug.user_id = u.user_id and ug.role_group_id = g.group_id)
where u.town in ('Paris','Rome') -- add more towns here
and json_extract(u.custom_metadata, '$.age') = 18
and ug.id is null;
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
I am working on a mysql query and its giving me headache!
The Scenario:
I am building a website where people can select industries they are interested in (NOTIFY_INDUSTRY). I join the selected values and store in a database field.
Example: a member selects agriculture (id = 9) and oil and gas (id = 13). I join them as 9-13 and store in the database.
Users can select several industries, not limited to two.
Also, members can select an industry (COMPANY_INDUSTRY) it belongs in assuming Information Technology which is stored in the database too.
Sample table (members):
ID
EMAIL
COMPANY_NAME
COMPANY_INDUSTRY
NOTIFY_INDUSTRY
The problem:
When a new user registers on the website, mail (the mails are sent on daily basis) is sent to existing users who have the new user's industry (COMPANY_INDUSTRY) as one of their interested industries (NOTIFY_INDUSTRY).
What i have done:
$sql="select id, email
from members
where notify_industry in (
select company_industry
from members
where datediff($today, date_activated) <= 1)"
This does not select the right members and i do not know the right way to go about it
EDIT - Exact Problem with current output:
Does not return any row, even when it should.
Assuming the new user's company_industry is 9, and there is an existing user with notify_industry: 10-9-20; it is meant to return the existing members email as the new member is in the existing member's categories of interest; but i get blanks
As #Shiplu pointed out, this is largely a normalization issue. Despite what some people seem to think, multi-value columns are murder to try to get right.
Your basic issue is:
You have members, who are interested in one or more companies/industries, which belong to one or more industries. You table structure should probably start as:
Industry
===============
id -- autoincrement
name -- varchar
Company
==============
id -- autoincrement
name -- varchar
Company_Industry
===============
companyId -- fk reference to Company.id
industryId -- fk reference to Industry.id
Member
===============
id -- autoincrement
name -- varchar
email -- varchar
Member_Interest_Industry
=========================
memberId -- fk reference to Member.id
industryId -- fk reference to Industry.id
Member_Interest_Company
========================
memberId -- fk reference to Member.id
companyId -- fk reference to Company.id
To get all companies a member is interested in (directly, or through an industry), you can then run something like this:
SELECT a.name, a.email, c.name
FROM Member as a
JOIN Member_Interest_Company as b
ON b.memberId = a.id
JOIN Company as c
ON c.id = b.companyId
WHERE a.id = :inputParm
UNION
SELECT a.name, a.email, d.name
FROM Member as a
JOIN Member_Interest_Industry as b
ON b.memberId = a.id
JOIN Company_Industry as c
ON c.industryId = b.industryId
JOIN Company as d
ON d.id = c.companyId
WHERE a.id = :inputParm
You should redesign the tables, as others have suggested.
However, barring that, there is a gross hack you can do:
SET sql_mode = 'ANSI';
SELECT notify_members.id, notify_members.email
FROM members notify_members
INNER JOIN members new_members
WHERE CURRENT_DATE - new_members.date_activated <= 1
AND
new_members.company_industry RLIKE ('[[:<:]](' || REPLACE(notify_members.notify_industry, '-', '|') || ')[[:>:]]');
Yuck. Basically, you turn 9-13 into the MySQL regular expression [[:<:]](9|13)[[:>:]], which matches 9, 13, 13-27-61, etc., but does not match 19-131 and the like. (This supports a compound COMPANY_INDUSTRY field, too.)
Use join SQL syntax rather than a select in style..
You need to join the members table to itself.
Currently:
select id, email
from members where notify_industry in
(select company_industry
from members
where datediff($today, date_activated) <= 1
)
Use this style:
select m1.id, m1.email
from members m1
inner join members m2 on m1.company_industry = m.notify_industry
where datediff($today, m2.date_activated) <= 1
Note the use of aliasing to m1 and m2 to help understand which id and emails are returned.
This may get a little ugly but you could try the following
WARNING This will make a Cartesian Product worthy of any Mad Scientist
SELECT NotifyIndustry.id,NotifyIndustry.email
FROM
(
SELECT CONCAT('-',COMPANY_INDUSTRY,'-') company FROM members
WHERE datediff($today, date_activated) <= 1)"
) CompanyIndustry
INNER JOIN
(
SELECT CONCAT('-', NOTIFY_INDUSTRY,'-') who_to_notify
FROM members
) NotifyIndustry
ON LOCATE(company,who_to_notify)>0;
probably not the fastest query ever but this should do the job:
select m_to_notify.id, m_to_notify.email
from members m_to_notify
join members m_new_member
on '-' || m_to_notify.notify_industry || '-'
like '%-' || m_new_member.company_industry || '-%'
where datediff($today, m_new_memberdate_activated) <= 1)
Scenario:
We have 5 users. (users table)
Each user has up to 10 imgs. (image table)
These 10 images can be ordered 1 – 10. (image table)
Each img can be listed in multiple categories (say there are 5 categories – birds, bees, bunnies, brains, belugas (category table connected to img table via table that stores img_ids and category_ids)
In searching through the categories, say someone chooses bees. The search should find the images in that category that is listed CLOSEST to the #1 img for all users. So if each user has 3 images in the bees category, ordered as numbers 4, 7 & 9, the search should show the 4th as its closest to the number 1.
The results I keep getting are all over the place and almost seems like it is choosing the images via WHEN they were added to the DB.
SELECT i.img_name, i.ordered, a.user_name, c.keyword, c.cat_id
FROM images AS i JOIN artists AS a USING (user_id)
JOIN img_cat_table AS im USING ( img_id )
JOIN catkeys AS c USING (cat_id)
WHERE ( cat_id = 3) // THE BEES ID #
GROUP BY user_id ORDER BY user_name DESC
I'm also not sure if you want to show all of the relevant images in the right order, or only the top one. Assuming that it is the latter situation, you will need to join to a subquery or view that returns the min rank for each user, category:
SELECT i.img_name, i.ordered, a.user_name, c.keyword, c.cat_id
FROM images AS i JOIN artists AS a USING (user_id)
JOIN img_cat_table AS im USING ( img_id )
JOIN catkeys AS c USING (cat_id)
JOIN (
SELECT user_id, min(img_rank) img_rank
FROM images AS i
JOIN artists AS a on i.user_id = a.user_id
JOIN img_cat_table AS im on im.img_id = i.img_id
JOIN catkeys AS c on c.cat_id = i.cat_id
WHERE ( cat_id = 3) ) x on x.user_id = a.user_id and x.img_rank = img_rank
WHERE c.cat_id = 3
I'm not sure what the name of the column that holds the image ranking is. I called it img_rank. Hopefully this will give you the idea
though if you can post the table structure and data, that will be great but Here is what I haved tried
SELECT i.img_name, i.ordered, a.user_name, c.keyword, c.cat_id
from (
select img_name, ordered, img_id, user_id from
images
group by user_id
order by user_img ) as i
JOIN artists AS a USING (user_id)
JOIN img_cat_table AS im USING ( img_id )
JOIN catkeys AS c USING (cat_id)
WHERE ( cat_id = 3) // THE BEES ID #
Try removing DESC from your ORDER BY clause.