MySQL Group running too slow - php

I have a 3 MySQL tables which I need to get results from and which are:
1. Towns
Fields Towncode and Townname
2. Students
Fields student_id,name,surname,address,streetcode,towncode,HeadOfFamily
3. Phonebank
Fields student_id,contacted,towncode
Now I need a mysql statement (a) to get the total number of households from the students table and also (b) the number of students contacted for that particular town.
up to step (a) I managed which and is extremely fast which is:
SELECT
t.towncode as towncode,
t.townname as townname,
(SELECT COUNT(*)
FROM students p
WHERE p.towncode=t.towncode
and p.student_hh='H') AS households
FROM
towns t
ORDER BY
t.towncode ASC
but I cannot manage to insert as well another SELECT STATEMENT to get the number of calls fr that particular town.
Can you kindly assist please?

Doing a per-record count on towns is probably hurting you. I would pre-query the students table first, THEN use that joined to the towns.
For indexes, I would have the following indexes
table index
towns ( towncode, townname )
students ( student_hh, towncode )
SELECT
t.towncode as towncode,
t.townname as townname,
TownCnts.households
FROM
towns t
JOIN ( SELECT
p.towncode,
COUNT(*) households
from
students p
where
p.student_hh = 'H'
group by
p.towncode ) as TownCnts
ON t.towncode = TownCnts.towncode
ORDER BY
t.towncode ASC

Assuming that the number of "calls" is the number of rows in the PhoneBank table with a particular town code, then you can add another subselect:
SELECT t.towncode as towncode, t.townname as townname,
(SELECT COUNT(*)
FROM students p
WHERE p.towncode = t.towncode and p.student_hh='H'
) AS households,
(SELECT COUNT(*)
FROM phonebank pb
WHERE pb.towncode = t.towncode
) AS calls
FROM towns t
ORDER BY t.towncode ASC ;
Your question is a little vague. It is possible that you want count(distinct studentid) in the second query, rather than count(*).
To optimize this query, create the following indexes:
towns(towncode)
students(towncode, student_hh)
phonebank(towncode)

Related

Order by inside loop PDO [duplicate]

I have a table which stores IDs and the city where the store is located.
I want to list all the stores starting with the stores that are in the city where there are the most stores.
TABLE
ID CITY
1 NYC
2 BOS
3 BOS
4 NYC
5 NYC
The output I want is the following since I have the most stores in NYC, I want all the NYC location to be listed first.
1 NYC
4 NYC
5 NYC
2 BOS
3 BOS
SELECT count(City), City
FROM table
GROUP BY City
ORDER BY count(City);
OR
SELECT count(City) as count, City
FROM table
GROUP BY City
ORDER BY count;
Ahh, sorry, I was misinterpreting your question. I believe Peter Langs answer was the correct one.
This one calculates the count in a separate query, joins it and orders by that count (SQL-Fiddle):
SELECT c.id, c.city
FROM cities c
JOIN ( SELECT city, COUNT(*) AS cnt
FROM cities
GROUP BY city
) c2 ON ( c2.city = c.city )
ORDER BY c2.cnt DESC;
This solution is not a very optimal one so if your table is very large it will take some time to execute but it does what you are asking.
select c.city, c.id,
(select count(*) as cnt from city c2
where c2.city = c.city) as order_col
from city c
order by order_col desc
That is, for each city that you come across you are counting the number of times that that city occurs in the database.
Disclaimer: This gives what you are asking for but I would not recommend it for production environments where the number of rows will grow too large.
SELECT `FirstAddressLine4`, count(*) AS `Count`
FROM `leads`
WHERE `Status`='Yes'
AND `broker_id`='0'
GROUPBY `FirstAddressLine4`
ORDERBY `Count` DESC
LIMIT 0, 8

SQL Left Join Return Latest Row

Two tables, with a left join. For ease table 1 and table 2.
Table 1 contains a list of people and their current status, table 2 is all of their "invites". All im trying to do as part of the join is show in a list all the current "people" and then the LATEST invite status (from table 2) so return a single row from table 2.
I have everything working... but its duplicating for example if a person has had multiple invites it will put them twice on the list. I just want to limit it to
$sql = "SELECT table1.fieldname as table1fielname table2.fieldname [more fields]
FROM xxx
LEFT JOIN xxx on table1.sharedid=table2.sharedid
WHERE XXX LIMIT 1 ";`
Obvioulsy the limit 1 doesnt do what its supposed to. I have tried adding additional select statements in brackets but being honest it just breaks everything and im not an expert at all.
I'm not an expert too but I'll try. Have you tried to use DISTINCT?
For exemple:
SELECT DISTINCT column_name1,column_name2
FROM table_name; [...]
It normally delete double matches.
Here are the links:
http://www.w3schools.com/sql/sql_distinct.asp
https://www.techonthenet.com/oracle/distinct.php
Give example data. And use good table and column names. For example:
(this returns all rows that satisfy the join):
WITH people(ppl_id,ppl_name,status) AS (
SELECT 1,'Arthur','active'
UNION ALL SELECT 2,'Tricia','active'
), invites(ppl_id,inv_id,inv_date) AS (
SELECT 1,1, DATE '2017-01-01'
UNION ALL SELECT 1,2, DATE '2017-01-07'
UNION ALL SELECT 1,3, DATE '2017-01-08'
UNION ALL SELECT 2,1, DATE '2017-01-01'
UNION ALL SELECT 2,2, DATE '2017-01-08'
)
SELECT
*
FROM people
JOIN invites USING(ppl_id)
ORDER BY 1
;
ppl_id|ppl_name|status|inv_id|inv_date
1|Arthur |active| 1|2017-01-01
1|Arthur |active| 3|2017-01-08
1|Arthur |active| 2|2017-01-07
2|Tricia |active| 2|2017-01-08
2|Tricia |active| 1|2017-01-01
But we want only 'Arthur' with '2017-01-08' and 'Tricia' with '2017-01-08'.
With any database that supports ANSI 99, you could try with a temporary table containing the newest invitation date per "people id", and join that temporary table with the invitations table. We call that table newest_invite_date, and, apparently, it does what we expect it to do:
WITH people(ppl_id,ppl_name,status) AS (
SELECT 1,'Arthur','active'
UNION ALL SELECT 2,'Tricia','active'
), invites(ppl_id,inv_id,inv_date) AS (
SELECT 1,1, DATE '2017-01-01'
UNION ALL SELECT 1,2, DATE '2017-01-07'
UNION ALL SELECT 1,3, DATE '2017-01-08'
UNION ALL SELECT 2,1, DATE '2017-01-01'
UNION ALL SELECT 2,2, DATE '2017-01-08'
), newest_invite_date(ppl_id,inv_date) AS (
SELECT ppl_id,MAX(inv_date)
FROM invites
GROUP BY ppl_id
)
SELECT
people.ppl_id
, people.ppl_name
, people.status
, newest_invite_date.inv_date
FROM people
JOIN newest_invite_date USING(ppl_id)
ORDER BY 1
;
ppl_id|ppl_name|status|inv_date
1|Arthur |active|2017-01-08
2|Tricia |active|2017-01-08
Is this what you were looking for?
Happy playing ...
Marco the Sane

Distinct sorting for distinct types in php

My table description is as follows
**entry_table**
- serial(int)
- s_name(varchar)
- user_id(int)
- id(int)
**Students_details**
- id(int)
- user_id(int)
- student_name(varchar)
- adress(varchar)
**User_login**
- user_id(int)
- user_name(varchar)
- password(varchar)
- alotment(bool)
Scenario is that the students apply for multiple scholarships. Their selections are stored in the entry_table's s_name, user_id and id fields.
My next step is to build a sorted list of all the students who applied for a particular scholarship eg:"scholarship1".
This list should also show the student's name(student_name field of the students_details table)
The lists are to be sorted according to two types of scholarships that the system offers(merit and need). Applicants of the merit scholarship are required to be sorted in descending order using the ratio(obtained marks/ total marks). However, the need scholarship is to be shorted in ascending order as it uses the ratio(family income/no. of non-earning family members)
I tried to join my tables using
$query = "SELECT *FROM entry_table, students_details
WHERE entry_table.id=students_details.id
group by entry.s_id,entry.student_id";
Please help in the sorting as per type problem. Also the above query helps joining the tables but doesnt achieve the purpose.
thanking you in advance
select * from entry_table inner join students_details on entry_table.id=students_details.id order by entry.student_id asc;
First thing you should connect tables properly.
$query = "SELECT sd.student_name, et.s_name FROM Student_details AS sd LEFT JOIN
entry_table AS et ON et.user_id = sd.user_id LEFT JOIN
user_login AS ul ON ul.user_id = sd.user_id
WHERE et.s_name = 'merit'
ORDER BY et.s_name DESC
UNION ALL
SELECT sd.student_name, et.s_name FROM Student_details AS sd LEFT JOIN
entry_table AS et ON et.user_id = sd.user_id LEFT JOIN
user_login AS ul ON ul.user_id = sd.user_id
WHERE et.s_name = 'need'
ORDER BY et.s_name ASC"
If you need a list you don't need to group, as far as you don't need a count number on any field or unique rows.
This is what I have in the top of my head, maybe do the trick I didn't test it, but maybe give you an idea.
I can't see the ratio field on your tables.
Use Join instead where to connect tables, makes more sense and improve query performance.
Next time, put SQL table code, is more useful to give a better response.
Sorry for my english!!!

How to compare tabels and get difference out

I have some problems to compare two of my tables. Usually I show my attempt how to solve a problem but here I do not know what to do. Normally, I do it in php. So i get all the information from 2 tables and then compare. But I would like to do it in MySQL. I hope you can help.
The first table, is my transactions table. This is the place where people have used their cards in restaurants.
The second table is my booking table. This is the place where people book reservations in restaurant.
I want to compare these tables, so i can get those who has NOT used their reservation
Transaction mySQL
SELECT t.*, em.* FROM transactions as t
left join exp_members as em on (t.cardid-10000000 = em.member_id)
left JOIN exp_member_data emd on em.member_id = emd.member_id ORDER BY t.created DESC
Transaction Table.
Trans_ID TransactionTime Name Restaurant
1852 2013-04-08 12:45:21 Christian La Canton
1851 2013-04-08 12:41:00 Zaz Parken
Booking mySQL
SELECT b.* from exp_menucard_booking as b;
Booking Table:
ID BookingTime Name NumberOfPeople Restaurant
270 2013-04-09 14:45:00 Christian 2 La Canton
269 2013-04-08 12:17:00 Toby 4 La Raz
As you can se, Toby from Booking table has not used his card (transaction tabel). How can i get him out of my tabel.
If you want to select all persons, who have already booked (there is an entry in booking table) but do not have an entry in the Transaction Table you could try this query:
SELECT *
FROM `Booking` B
WHERE NOT EXISTS (
SELECT *
FROM `Transaction` T
WHERE T.name = B.name
);
BTW it would be better, if you used some User_ID instead of name to identify the persons
This is how you can select all the records that didn't use the booking. It's called an excluding left join or left exclude join. http://www.magecorner.com/mysql-joins-explained/
I'm not sure if this would be faster than WHERE NOT EXISTS, but considering that has a subquery, I think this is faster.
SELECT emb.*
FROM exp_menucard_booking emb
LEFT JOIN transactions t
ON (
t.Name = emb.Name AND
t.Restaurant = emb.Restaurant
)
WHERE t.Trans_ID IS NULL
SELECT * FROM Booking WHERE NAME NOT IN (SELECT NAME FROM Transaction );

MySQL Join and create new column value

I have an instrument list and teachers instrument list.
I would like to get a full instrument list with id and name.
Then check the teachers_instrument table for their instruments and if a specific teacher has the instrument add NULL or 1 value in a new column.
I can then take this to loop over some instrument checkboxes in Codeigniter, it just seems to make more sense to pull the data as I need it from the DB but am struggling to write the query.
teaching_instrument_list
- id
- instrument_name
teachers_instruments
- id
- teacher_id
- teacher_instrument_id
SELECT
a.instrument,
a.id
FROM
teaching_instrument_list a
LEFT JOIN
(
SELECT teachers_instruments.teacher_instrument_id
FROM teachers_instruments
WHERE teacher_id = 170
) b ON a.id = b.teacher_instrument_id
my query would look like this:
instrument name id value
--------------- -- -----
woodwinds 1 if the teacher has this instrument, set 1
brass 2 0
strings 3 1
One possible approach:
SELECT i.instrument_name, COUNT(ti.teacher_id) AS used_by
FROM teaching_instrument_list AS i
LEFT JOIN teachers_instruments AS ti
ON ti.teacher_instrument_id = i.id
GROUP BY ti.teacher_instrument_id
ORDER BY i.id;
Here's SQL Fiddle (tables' naming is a bit different).
Explanation: with LEFT JOIN on instrument_id we'll get as many teacher_id values for each instrument as teachers using it are - or just a single NULL value, if none uses it. The next step is to use GROUP BY and COUNT() to, well, group the result set by instruments and count their users (excluding NULL-valued rows).
If what you want is to show all the instruments and some flag showing whether or now a teacher uses it, you need another LEFT JOIN:
SELECT i.instrument_name, NOT ISNULL(teacher_id) AS in_use
FROM teaching_instrument_list AS i
LEFT JOIN teachers_instruments AS ti
ON ti.teacher_instrument_id = i.id
AND ti.teacher_id = :teacher_id;
Demo.
Well this can be achieved like this
SELECT
id,
instrument_name,
if(ti.teacher_instrument_id IS NULL,0,1) as `Value`
from teaching_instrument_list as til
LEFT JOIN teachers_instruments as ti
on ti.teacher_instrument_id = til.id
Add a column and check for teacher_instrument_id. If found set Value to 1 else 0.

Categories