Generating SQL Query - mysql

have the following DB structure in MySQL:
Table1: ORDER
With Attributes
ID (PRIMARY KEY)
DELIVERY_DATE
CUSTOMER_ID
RESTAURANT_ID
ORDER_VALUE
Table2 : CUSTOMER
With Attributes
ID (PRIMARY KEY)
NAME
LOGIN
Table 3: Restaurant
ID (PRIMARY KEY)
NAME
CITY
I want to generate a db query where I select the customer_ID, login, Delivery_date, Sequence number, And the number of days from the last order to the second to last order.
I am having trouble using Inner join and generating the last two columns of my query:
Sequence: Each user may have ordered any number of dishes, I want to show a number three if it is the third time he orders, I should be able to get it from his customer_ID and the Order_ID
DateDiff from last order: I am having trouble getting the last and the second to last order and doing a dateDIFF and populating the column
I have so far constructed the following query:
SELECT customer_id,
login,
delivery_date,
sequence,
Datediff(second_to_last_order, last_order)
FROM order AS t1
INNER JOIN customer AS t2
ON t1.customer_id = t2.id
INNER JOIN restaurant AS t3
ON t1.restaurant_id = t3.id;
But it is obviously incomplete but i am stuck, any suggestions?

If this is MySQL Server query as you mentioned then I'd suggest you give the following a go;
SELECT CUSTOMER_ID, LOGIN, DELIVERY_DATE, SEQUENCE, DATEDIFF((SELECT MAX(z.ORDER_DATE) FROM ORDER AS z WHERE z.CUSTOMER_ID = t1.CUSTOMER_ID), (SELECT MAX(x.ORDER_DATE) FROM ORDER AS x WHERE x.CUSTOMER_ID = t1.CUSTOMER_ID AND x.ID != (SELECT MAX(x.ID) FROM ORDER AS x2 WHERE x2.CUSTOMER_ID = t1.CUSTOMER_ID)))
FROM ORDER AS a
INNER JOIN CUSTOMER AS t2 ON t1.CUSTOMER_ID = t2.ID
INNER JOIN RESTAURANT AS t3 ON t1.RESTAURANT_ID = t3.ID
ORDER BY t1;
I would advice that it's good practise to not call a table "ORDER" as this is a keyword used by MySQL, you could instead simple call the table "ORDERS" to avoid the issue.
This should do what you are asking for, if you have any issue please post any error text so we can better understand what you are facing.

Related

MySQL Get exactly one entry per id by multiple ids

my problem is that I want this:
SELECT * FROM table
WHERE userId = 7243
ORDER BY date desc LIMIT 1
But for multiple ids in one request.
I tried this:
SELECT * FROM table
WHERE userId IN (7243, 1)
GROUP BY userId
ORDER BY date desc
But the order by seems to be ignored. Do anyone has a solution for me? Thank you
If you want the max date record for each of the two IDs, then you may use a subquery:
SELECT t1.*
FROM yourTable t1
INNER JOIN
(
SELECT userId, MAX(date) AS max_date
FROM yourTable
WHERE userId IN (7243, 1)
GROUP BY userId
) t2
ON t1.userId = t2.userId AND t1.date = t2.max_date
WHERE
t1.userId IN (7243, 1);
This is the just greatest-value-per-group question with a slight twist, namely that you only want to see two of the possible groups in the output.
As #Raymond commented below, an index on (userId, date) should greatly speed up the t2 subquery. I am not sure if this index would help beyond that, but it should make a difference.

MySQL Select From Multiple Tables + Last row from somewhere else

Hello I have these 4 tables with the structure
Table: Users [ id, username, password, bouquet_id ]
Table: Bouquets [ id, bouquet_name, stream_ids = serialized array ]
Table: Streams [ id, channel_name ]
Table: activity [ id, user_id, stream_id ]
I want to select ALL users but with their info as well from other tables + THE LAST ROW from table activity per user
For example the following query:
SELECT t1.*,t2.`bouquet_name`
FROM `users` t1,`bouquets` t2
WHERE t1.`bouquet_id ` = t2.`id`
ORDER BY t1.id DESC
Takes the data from the first 2 tables and assigned the bouquet_id to its bouquet name.
Now i want to have in query the last ROW from activity table WITH it's stream name [based on stream_id]
The following query does the job i want[ PER USER]
SELECT t1.channel_name
FROM `streams` t1,`activity` t2
WHERE t2.user_id = **'%d'** AND t1.id = t2.stream_id
ORDER BY t2.id DESC
LIMIT 1
But its a kind slow since for every user in the table "users" i run 2 queries.
I want the 2 queries above to be embed together as one so that i will be able to select the data from the first two tables BUT WITH the last row from table activity based on user_id.
Hope you understand me
thank you
You should not depend on "last row" = "row with greatest id". Since MySQL provides the possibility to setup master-master replications, which then offers the possibility to assign auto_increment values in more or less arbitrary order, this assumption is not always true. An additional timestamp column would be better.
You can select the most recent activity per user for example with:
SELECT user_id, MAX(id) FROM activity GROUP BY user_id
You then have to join this into your query and join in the basic activity table to retrieve the data you actually want. You may want to replace your joins with left joins too, so you will always retrieve all users regardless if there exists rows in bouquets or activity:
SELECT t1.*, t2.`bouquet_name`, activity.*
FROM `users` t1
LEFT JOIN `bouquets` t2
ON t1.`bouquet_id ` = t2.`id`
LEFT JOIN (
SELECT user_id, MAX(id) AS maxid FROM activity GROUP BY user_id
) mostrecent
ON t1.user_id = mostrecent.user_id
LEFT JOIN activity
ON t1.user_id = activity.user_id AND activity.id = mostrecent.maxid
ORDER BY t1.id DESC
And then you can join in the stream data too like
SELECT t1.*, t2.`bouquet_name`, activity.stream_id, streams.channel_name
FROM `users` t1
LEFT JOIN `bouquets` t2
ON t1.`bouquet_id ` = t2.`id`
LEFT JOIN (
SELECT user_id, MAX(id) AS maxid FROM activity GROUP BY user_id
) mostrecent
ON t1.user_id = mostrecent.user_id
LEFT JOIN activity
ON t1.user_id = activity.user_id AND activity.id = mostrecent.maxid
LEFT JOIN streams
ON activity.stream_id = streams.id
ORDER BY t1.id DESC

MySQL get one record per group

I have a MySQL table targeted_refills with columns: id (unique, primary key), pat_name, pat_phone, rx_number, drug_name, qty_disp, last_date, qty_left, price_code, last_price, last_contact, doc_name, ehf, userid
What I'm trying to do is list one record for each userid. The record I want listed is the most recent one based on the field last_date however if there is more than one record for that user with the most recent date then I want it to select the field with the largest value in the field last_price. In the rare event that there are two records with matching last_date and last_price (shouldn't happen but is possible), I really don't care which is chosen so it can be chosen off the largest value of id since that is unique and the primary key.
EDIT:
I've tried this query that I pieced together from another person's question:
SELECT t1.*
FROM `targeted_refills` AS t1
LEFT OUTER JOIN `targeted_refills` AS t2
ON t1.userid = t2.userid
AND (t1.last_date < t2.last_date
OR (t1.last_date = t2.last_date AND t1.last_price < t2.last_price))
WHERE t2.userid IS NULL;
and it does a reasonable job of getting where I want to go, but it's pulling a few more records than I want. I'm comparing now to see what extra records it's pulling and why. It also takes a long time to run.
UPDATED You can tweak your existing query a little bit: you need at least one more join condition that prevent duplicates when both last_date and last_price are equal across several rows. Since you have auto-generated id you can use it to differentiate rows.
SELECT t1.*
FROM targeted_refills t1 LEFT JOIN targeted_refills t2
ON t1.userid = t2.userid
AND (
t1.last_date < t2.last_date
OR
(t1.last_date = t2.last_date AND
t1.last_price < t2.last_price)
OR
(t1.last_date = t2.last_date AND
t1.last_price = t2.last_price AND
t1.id < t2.id)
)
WHERE t2.userid IS NULL;
Or you can achieve your goal another way
SELECT id, pat_name, pat_phone, rx_number, drug_name,
qty_disp, last_date, qty_left, price_code,
last_price, last_contact, doc_name, ehf, userid
FROM
(
SELECT r.*,
#n := IF(#u = userid, #n + 1, 1) rownum,
#u := userid
FROM targeted_refills r
ORDER BY userid, last_date DESC, last_price DESC
) q
WHERE q.rownum = 1
Here is SQLFiddle demo for both queries.

MySQL merging two queries one with group by

I have two tables, one holds user info (id, name, etc) and another table that holds user tickets and ticket status (ticket_id, user_id, ticket_status, etc).
I want to produce a list of ALL the users for example: ( SELECT * FROM user_table )
And for each user I need a count of their tickets for example:
(SELECT t1.user_id, COUNT(*) FROM user_tickets t1 WHERE t1.ticket_status = 15 GROUP BY t1.ticket_status, t1.user_id )
I can do this query to achieve what I’m looking for but it takes 5sec. to run the query on 50000 tickets, while each query running separately only takes fraction of a second.
SELECT t1.user_id, COUNT(*)
FROM user_tickets t1
LEFT JOIN user_table t2 ON t1.user_id = t2.id
WHERE t2.group_id = 20 AND t1.status_id = 15
GROUP BY t1.status_id, user_id
Any idea how to write the query to get same performance as each separately?
An indexing where clause fixed the problem.

How do I write this kind of query (returning the latest avaiable data for each row)

I have a table defined like this:
CREATE TABLE mytable (id INT NOT NULL AUTO_INCREMENT, PRIMARY KEY(id),
user_id INT REFERENCES user(id) ON UPDATE CASCASE ON DELETE RESTRICT,
amount REAL NOT NULL CHECK (amount > 0),
record_date DATE NOT NULL
);
CREATE UNIQUE INDEX idxu_mybl_key ON mytable (user_id, amount, record_date);
I want to write a query that will have two columns:
user_id
amount
There should be only ONE entry in the returned result set for a given user. Furthermore, the amount figure returned should be the last recoreded amount for the user (i.e. MAX(record_date).
The complication arises because weights are recorded on different dates for different users, so there is no single LAST record_date for all users.
How may I write (preferably an ANSI SQL) query to return the columns mentioned previously, but ensuring that its only the amount for the last recorded amount for the user that is returned?
As an aside, it is probably a good idea to return the 'record_date' column as well in the query, so that it is eas(ier) to verify that the query is working as required.
I am using MySQL as my backend db, but ideally the query should be db agnostic (i.e. ANSI SQL) if possible.
First you need the last record_date for each user:
select user_id, max(record_date) as last_record_date
from mytable
group by user_id
Now, you can join previous query with mytable itself to get amount for this record_date:
select
t1.user_id, last_record_date, amount
from
mytable t1
inner join
( select user_id, max(record_date) as last_record_date
from mytable
group by user_id
) t2
on t1.user_id = t2.user_id
and t1.record_date = t2.last_record_date
A problem appears becuase a user can have several rows for same last_record_date (with different amounts). Then you should get one of them, sample (getting the max of the different amounts):
select
t1.user_id, t1.record_date as last_record_date, max(t1.amount)
from
mytable t1
inner join
( select user_id, max(record_date) as last_record_date
from mytable
group by user_id
) t2
on t1.user_id = t2.user_id
and t1.record_date = t2.last_record_date
group by t1.user_id, t1.record_date
I do not now about MySQL but in general SQL you need a sub-query for that. You must join the query that calculates the greatest record_date with the original one that calculates the corresponding amount. Roughly like this:
SELECT B.*
FROM
(select user_id, max(record_date) max_date from mytable group by user_id) A
join
mytable B
on A.user_id = B.user_id and A.max_date = B.record_date
SELECT datatable.* FROM
mytable AS datatable
INNER JOIN (
SELECT user_id,max(record_date) AS max_record_date FROM mytable GROUP BS user_id
) AS selectortable ON
selectortable.user_id=datatable.user_id
AND
selectortable.max_record_date=datatable.record_date
in some SQLs you might need
SELECT MAX(user_id), ...
in the selectortable view instead of simply SELECT user_id,...
The definition of maximum: there is no larger(or: "more recent") value than this one. This naturally leads to a NOT EXISTS query, which should be available in any DBMS.
SELECT user_id, amount
FROM mytable mt
WHERE mt.user_id = $user
AND NOT EXISTS ( SELECT *
FROM mytable nx
WHERE nx.user_id = mt.user_id
AND nx.record_date > mt.record_date
)
;
BTW: your table definition allows more than one record to exist for a given {id,date}, but with different amounts. This query will return them all.