how to structure a mysql subquery - mysql

I have a users table, and an appointments table. For any given day, I would like a query that selects
1) the user_id the appointment is scheduled with
2) the number of appointments for that user for the specified day.
It seems I can one or the other, but I'm unsure of how to do it with one query. For instance, I can do:
SELECT user_id FROM appt_tbl WHERE DATE(appt_date_time) = '2012-10-14'
group by user_id;
Which will give me the users that have an appointment that day, but how can I add to this query another column that will give me how many appointments each user has? Assuming I need some kind of subquery, but I'm unsure of how to structure that.

SQL uses the notion of "aggregate functions" to get you this information. You can use them with any aggregating query (i.e. it has "group by" in it).
SELECT user_id, count(*) as num_apts ...

Try adding COUNT(*) to your query:
SELECT user_id, COUNT(*) FROM appt_tbl WHERE DATE(appt_date_time) = '2012-10-14'
group by user_id;

Related

Multiple select statement on single table

What I want to achieve is give a user the ability to query the database for particular transactions of value say "34666" & name within a given period. Something like:
SELECT * FROM (SELECT * FROM Ledger WHERE transactiondate BETWEEN '2018-01-01' AND '2018-10-10') WHERE name='Customer' OR surnname='Customer' OR fullnames LIKE %Customer% AND (credit="34666" OR debit="34666") sub ORDER BY transactionid ASC;
But the above obviously is not right statement. Just to give an idea of what I want to achieve. Getting an "#1248 - Every derived table must have its own alias" error message with above. Tried using alias but am not really good with complex MySql queries and got it all muddled up.
Table has columns:
transactionid
transactiondate
name
surname
fullnames
credit
debit
amount
reference
Want to loop through and output every row matching the query.
Thanks.
I don't think you need a subquery here at all, I don't know why you thought to use one:
SELECT *
FROM Ledger
WHERE
transactiondate BETWEEN '2018-01-01' AND '2018-10-10' AND
(name = 'Customer' OR surnname = 'Customer' OR fullnames LIKE '%Customer%') AND
(credit = '34666' OR debit = '34666')
ORDER BY
transactionid;
The exact cause of your error is that your query has a subquery without an alias. There does appear to be an alias sub in there, but it is out place. It should have appeared right after the end of the subquery. But again, I don't think you need a subquery here.

How can I exclude duplicate records from sql query

I am trying to create a report that basically pulls all data from a sql table. The problem is this table was used for registration purposes and there are cases where the same employee registered for two different events and so there are multiple entries for them in the table. I really don't need to know which events they registered for, just that they did register. Is there any way to modify my query so that only one record per employee is selected? I tried to use DISTINCT but that still pulled every single record.
The explanation for what you are seeing most likely is that the records you are getting back are not duplicates. For example, if you used SELECT *, then you would be getting back all columns, and even though the employee name, id, etc. columns could be identical for multiple records, the other columns might be distinct.
Without knowing your table structure, I cannot give an exact answer. But here are two ways to deal with this. First, you can use SELECT DISTINCT with only columns corresponding to the employee, e.g.
SELECT DISTINCT firstName,
lastName,
id
FROM yourTable
This has the disadvantage that it throws away information from all the other columns. If you want to retain those other columns, then a second option would be to group records by employee, and then use aggregate functions on the other columns, e.g.
SELECT firstName,
lastName,
id,
MIN(signupTime)
FROM yourTable
GROUP BY firstName,
lastName,
id
In this query, I assumed there is a timestamp column called signupTime, which recorded the time for each employee signup (including possible duplicate signups). This query would retain the first signup time, along with the employee id and name.
Use ROW_NUMBER() for this purpose
;WITH CTE AS (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY Name ORDER BY ID) RN
FROM Table
)
SELECT *
FROM CTE
WHERE RN=1
you should grouping them based on fullname, employeeid or whatever... . I have mentioned a simple sample to show you how to use group by command in SQL queries :
SELECT column_name, aggregate_function(column_name)
FROM table_name
WHERE column_name operator value
GROUP BY column_name;
I hope it works for you.
So by reading the question I assume you just want the same employee name to show up once no matter whether they have registered for more than 1 or 2 events.
For example:
$query_employee = mysql_query("SELECT employee_name, events FROM tbl GROUP BY employee_name");

How to remove a subquery from the FROM field in MySQL

I'm new to MySQL and databases and I've seen in many places that it is not considered a good programming practice to use subqueries in the FROM field of SELECT in MySQL, like this:
select userid, avg(num_pages)
from (select userid , pageid , regid , count(regid) as num_pages
from reg_pag
where ativa = 1
group by userid, pageid) as from_query
group by userid;
which calculates the average number of registers per page that the users have.
The reg_page table looks like this:
Questions:
How to change the query so that it doesn't use the subquery in the FROM field?
And is there a general way to "flatten" queries like this?
The average number of registers per page per user can also be calculated as number of registers per user divided by number of pages per user. Use count distinct to count only distinct psgeids per user:
select userid, count(regid) / count(distinct pageid) as avg_regs
from reg_pag
where ativa=1
group by userid;
There is no general way of flattening such queries. It may not even be possible to flatten some of them, otherwise there would be little point in having this feature in the first place. Do not get scared of using subqueries in the from clause, in some occasions they may be even more effective, than a flattened query. But this is vendor and even version specific.
One way is to use count(distinct):
select userid, count(*) / count(distinct pageid)
from reg_pag
where ativa = 1
group by userid;

SQL GROUP BY selecting

Here is my code:
mysql_query("SELECT * FROM messages WHERE to_user_id = '$user_id' GROUP BY from_user_id ORDER BY read,sent_date DESC")
and here is my table structure
I use the GROUPY BY from_user_id statement to briefly show a list of "conversations" instead of every single message. Like this
But, as you can see in the image, the top two are the wrong way round, the first one says "1 week ago" and the one below says "2 days ago". The reason for these being in the wrong order is due to the GROUP BY from_user_id statement. Because it groups all the messages from that user and it wont have the the most recent time on it.
So my question is:
How can I GROUP BY from_user_id by the most recent record?
You can not SELECT columns not listed in the GROUP BY or otherwise allowed functions. This is how GROUP BY works.
Although most other SQL flavors would fail on such a query, MySQL doesn't. Instead it provides arbitrary results - what you are seeing.
You can solve this a few different ways. For example:
SELECT the ids of the latest user conversations using GROUP BY in a separate query.
Do it all in one query by sub-selecting the ids or JOIN the set of ids.
Since MySQL doesn't support windowing functions like ROW_NUMBER() OVER() you can do something like this:
SELECT *
FROM Messages
where id in (SELECT MAX(ID) FROM messages GROUP BY from_user_id)
ORDER BY sent_date, read
The subquery will only return the newest message id for each user. I'm assuming your auto_increment corresponds with the order the messages are sent in. Even if it's not the exact logic you might want, this is a general technique to get a specific subset of values from grouped records that works in MySQL.
Try with this
SELECT *,MAX(id) as OrderField FROM messages WHERE to_user_id = '$user_id'
GROUP BY from_user_id
ORDER BY OrderField DESC,read

Is it wrong to have count(*) as part of the select query

select id, first_name, count(*) from users;
The users table contains 10 entries, but the above select query shows only a single row. Is it wrong to mix count(*) as part of the above query?
COUNT is a function that aggregates. You can't mix it into your normal query.
If you want to receive the ten entries just do a normal select:
SELECT id, name FROM users;
and to get the number of entries:
SELECT COUNT(id) FROM users;
Its becuase you are using an aggregate function in the select part of the query,
to return the 10 records you just need the id, and first_name in the query.
EG:
SELECT id, first_Name
FROM users
if you wanted to get a count of the records in the table then you could use
SELECT (Count(id))
FROM [users]
It's not "wrong", but it is meaningless without a "group by" clause - most databases will reject that query, as aggregate functions should include a group by if you're including other columns.
Not sure exactly what you're trying to achieve with this?
select id, first_name,(select count(*) from users) AS usercount from users;
will give each individual user and the total count but again, not sure why you would want it.
select id, first_name from users,(select count(*) as total from users) as t;
COUNT is an aggregate function and it will always give you count of all records in table unless used in combination with group by.
If you use it in combination with normal query, then it will take priority in deciding the final output as in your case it returns 1.
If you want to return all 10 records, you should just write -
select id,first_name from users
If you need number of rows in a table, you can use MySQL's SQL_CALC_FOUND_ROWS clause. Check MySQL docs to see how it's used.