How to display values from different tables in certain time frame MYSQL - mysql

I have attached the tables that are included in this question for MYSql. My question states:
First and last names of the top 5 authors clients borrowed in 2017. My code so far:
SELECT BookID,BorrowDate COUNT(BookID) AS BookIDCount
FROM Borrower
WHERE BorrowDate = 2017
ORDER BY BookIDCount DESC
LIMIT 5
I think so far my code just displays the top 5 Author ID in 2017 but I can't figure out how to display the names. I see the link between AuthorID and BookAuthor (maybe). Thank you so much for any help you may provide.
Here are the tables:

You can bring the client table with a join. I think that you want:
select c.clientFirstName, c.clientLastName, count(*) no_books
from borrower b
inner join client c on c.clientId = b.clientId
where b.borrowDate >= '2017-01-01' and b.borrowDate < '2018-01-01'
group by c.clientId, c.clientFirstName, c.clientLastName
order by count(*) desc
limit 5
This treats borrowDate as a column of type date (or the-like), because that what it seems to be. If it just a number that represent the year, then you can change back the where clause to your original condition.

Related

SQL Join with data associated to dates

Currently I have a simple SQL request to get aall group departure date and the associated group size (teamLength) between 2 dates but it doesn't work properly.
SELECT `groups`.`departure`, COUNT(`group_users`.`group_id`) as 'teamLength'
FROM `groups`
INNER JOIN `group_users`
ON `groups`.`id` = `group_users`.`group_id`
WHERE departure BETWEEN '2017-03-01' AND '2017-03-31'
In fact, if I have more than 1 group between the 2 dates, only 1 date will be recovered in association with the total number of teamLength.
For exemple, if I have 2 groups in the same interval with, for group 1, 2 people and for group 2, 1 people, the result will be:
Here are 2 screenshots of the current state of my groups and group_users tables:
Is it even possible to do what I want in only 1 SQL request ? Thanks
In addition to what jarlh commented (JOIN with ON). Don't ever group data without an explicit GROUP BY. I don't know why MYSQL still allows this...
Change your query to something like this and you should get the result you are looking for. Currently, the other departure dates get lost in the aggregation.
SELECT
groups.departure,
COUNT(1) as team_length
FROM
groups
INNER JOIN group_users
ON groups.id = group_users.group_id
WHERE
groups.departure BETWEEN '2017-03-01' AND '2017-03-31'
GROUP BY
groups.departure
I think that you have a syntax issue in your query. You are missing the ON statement so your database could be trying to get a cartesian product since there is no join clause.
SELECT `groups`.`departure`, COUNT(`group_users`.`id`) as 'teamLength'
FROM `groups`
INNER JOIN `group_users` ON `groups`.`id` = `group_users`.`group_id`
WHERE departure BETWEEN '2017-03-01' AND '2017-03-31'
GROUP BY `groups`.`departure`
You also are missing the GROUP BYclause which is not mandatory in all RDBS but it is a good practice to set it.

Merging 3 Tables, Limiting 1 Table With Multiple Fields Needed

Been looking into this for awhile. Hoping someone might be able to provide some insight. I have 3 tables. All of which I'm grabbing multiple columns, but the 3rd I need to limit the output to just the most recent timestamp entry, BUT still display multiple columns.
If I have the following data [ Please see SQL Fiddle ]:
http://sqlfiddle.com/#!2/84b91/6
The fiddle is a list of (names) in Table1(users), (job_name,years) in Table2(job), and then (score, timestamp) in Table3(job_details). All linked together by the users id.
I am definitely not great at MYSQL. I know I'm missing something.. possibly a series of JOINs. I have been able to get Table 1, Table 2 and one column of Table 3 by doing this:
select a.id, a.name, b.job_name, b.years,
(select c.timestamp
from job_details as c
where c.user_id = a.id
order by c.timestamp desc limit 1) score
from users a, job as b where a.id = b.user_id;
At this point, I can get multiple column data on the first two columns, limit the 3rd to one value and sort that value on the last timestamp...
My question is: How does one go about adding a second column to the limit? In the example in the fiddle, I'd like to add the score as well as the timestamp to the output.
I'd like the output to be:
NAME, JOB, YEARS, SCORE, TIMESTAMP. The last two columns would only be the last entry in job_details sorted by the most recent TIMESTAMP.
Please let me know if more information is required! Thank you for your time!
T
Try this:
select a.id, a.name, b.job_name, b.years, c.timestamp, c.score
from users a
INNER JOIN job as b ON a.id = b.user_id
INNER JOIN (SELECT jd.user_id, jd.timestamp, jd.score
FROM job_details as jd
INNER JOIN (select user_id, MAX(timestamp) as tstamp
from job_details
GROUP BY user_id) as max_ts ON jd.user_id = max_ts.user_id
AND jd.timestamp = max_ts.tstamp
) as c ON a.id = c.user_id
;

Outputting customers who haven't repeat ordered since a specific date range in MySQL

I have searched around but have not found anything which seems to be totally relevant so I'm asking for some help.
I have a single table which contains orders, people can have multiple orders. I need to be able to select people who purchased in 2 years ago (April 2012) where they haven't purchased since, to identify unique people I am using email address as there is no unique customer ID. Here is the order table fields:
orderid,order date,name,email
I have limited SQL knowledge, but my approach previously has been to output a table of orders from April 2012 and another table from May 2012 to the present date. I then compare the 2 tables to find customers who haven't ordered since.
I can't help but feel like there is a more efficient way to do this in one query. Can anyone help?
Thanks,
Sam.
I would use LEFT JOIN here:
SELECT DISTINCT order1.email
FROM `order` order1
LEFT JOIN (SELECT email FROM `order` WHERE orderdate >= '2012-05-01') order2
ON order1.email = order2.email
WHERE orderdate < '2012-05-01'
AND order2.email IS NULL;
The main query is checking for people who ordered before May 2012, and the LEFT JOIN query is checking (with order2.email IS NULL) for people who have NOT ordered since then.
You can write this query without any join something like this:-
select email
from order
where email not in (select email from order where order_date >"April 2012")
and order_date < "April 2012";
Here, first condition filter outs all customer who have bought in last two years, and from second condition you get the customers who have bought two years ago.
As suggested, query with NOT EXISTS will go like this:
select o1.email
from order o1
where not exists (select 1 from order o2 where o2.order_date > "April 2012" and o1.email = o2.email)
and o1.order_date < "April 2012";

Group by week, display empty weeks - with where clause

We are attempting to count how many unique telephone numbers called a particular number each day, with 0 (or NULL) for days with no calls. To simplify the data schema, our table contains four fields:
| id | fromz | toz | date |
fromz: inbound call number
toz: number called
date: yyyy-mm-dd
When all we need to know is how many unique numbers called by each day - and do not care what number was called - it is simple to include no-calls days in results.
We JOIN to another table containing only sequential dates: calendar
| id | date |
date:yyyy-mm-dd
SELECT p.fromz, count(unique(p.fromz)), p.date FROM phones p
RIGHT OUTER JOIN calendar c ON
p.date = c.date
GROUP BY c.date
If no p.fromz on c.date, the result for that p.date is "0" (or NULL)
Problem arises when we begin to sort by numbers called:
SELECT p.fromz, count(unique(p.fromz)), p.date FROM phones p
RIGHT OUTER JOIN calendar ON
p.date = c.date
WHERE p.toz = "#myNumber"
GROUP BY c.date
Because there are no WHERE toz = #myNumber on some days we only get results on days when (WHERE) there were calls to #myNumber.
Any suggestions?
I've modified some of your field names slightly. I'm not sure if this 100% meets your use-case, but if it doesn't, you should be able to figure it out from here. Basically, move your current WHERE condition into the ON clause.
SELECT p.fromz, count(distinct p.fromz), p.callDate, c.theDate FROM phones p
RIGHT OUTER JOIN calendar c ON
p.callDate = c.theDate AND p.toz = 2125555555
GROUP BY c.theDate
SQLFiddle
Full Disclosure
This is not standards-compliant. For a compliant query, take a look at Ollie's answer and simply move the p.toz part from the WHERE to the ON to get the results you want.
You are very close.
You're using a nasty MySQL nonstandard extension to GROUP BY and it is driving you crazy, as it has driven many others crazy before you.
http://dev.mysql.com/doc/refman/5.5/en/group-by-extensions.html
You need to follow the rules for standard GROUP BY. These rules specify that each column mentioned in the SELECT clause must be either (1) also mentioned in the GROUP BY clause or (2) an aggregate function.
There are two problems in your query. One is that you mention p.date instead of c.date in your SELECT clause. That p.date will be null if there aren't any items on the date in question in your phones table. The second item is that you're mentioning p.fromz twice.
I think your basic aggregate query should look like this http://sqlfiddle.com/#!2/0ef4e/3/0:
SELECT p.toz,
COUNT(DISTINCT p.fromz) AS unique_calling_number_count,
c.date
FROM phones AS p
RIGHT OUTER JOIN calendar AS c ON p.date = c.date
GROUP BY p.toz, c.date
ORDER BY c.date, p.toz
That will summarize call-origination numbers by day and call-destination numbers.
Then, when you need to filter your phones records according to some kind of criterion, do that in a subquery as follows.
SELECT p.toz,
COUNT(DISTINCT p.fromz) AS unique_calling_number_count,
c.date
FROM (
SELECT * /* filtering subquery */
FROM phones
WHERE toz = "#myNumber"
) AS p
RIGHT OUTER JOIN calendar AS c ON p.date = c.date
WHERE p.toz = "#myNumber"
GROUP BY p.toz, c.date
ORDER BY c.date, p.toz
Here's an example of this working. http://sqlfiddle.com/#!2/0ef4e/2/0 Thanks to #PatrickQ for noticing the WHERE at the wrong level of the query.

Select corresponding records from another table, but just the last one

I have 2 tables authors and authors_sales
The table authors_sales is updated each hour so is huge.
What I need is to create a ranking, for that I need to join both tables (authors has all the author data while authors_sales has just sales numbers)
How can I create a final table with the ranking of authors ordering it by sales?
The common key is the: authorId
I tried with LEFT JOIN but I must be doing something wrong because I get all the authors_sales table, not just the last.
Any tip in the right direction much appreciated
If you're looking for aggregate data of the sales, you'd want to join the tables, group by the authorId. Something like...
select authors.author_id, SUM(author_sales.sale_amt) as total_sales
from authors
inner join author_sales on author_sales.author_id = authors.author_id
group by authors.author_id
order by total_sales desc
However (I couldn't distinguish from your question whether the above scenario or next is true), if you're only looking for the max value of the author_sales table (if the data in this table is already aggregated), you can join on a nested query for author_sales, such as...
select author.author_id, t.sales from authors
inner join
(select top 1 author_sales.author_id,
author_sales.sale_amt,
author_sales.some_identifier
from author_sales order by some_identifier desc) t
on t.author_id = author.author_id
order by t.sales desc
The some_identifier would be how you determine which record is the most recent for author_sales, whether it is a timestamp of when it was inserted or an incremental primary key, however it is set up. Depending on if the data in author_sales is aggregated already, one of these two should do it for you...
select a.*, sum(b.sales)
from authors as a
inner join authors_sales as b
using authorId
group by b.authorId
order by sum(b.sales) desc;
/* assuming column sales = total for each row in authors_sales */