Get Column From Subquery - mysql

I currently have the following (simplified) sql which selects user data from a main table table1. This query only select users who have not logged in since a given date. I use a sub query as the login table is separate from the main table. The two tables relate via a user ID column. This works perfectly.
// sql to get all records that haven't logged in since date above
$sql = "SELECT t1.*
FROM table1 t1
WHERE '2016-08-01' > (SELECT MAX(t2.DateCol)
FROM table2 t2
WHERE t2.userId= t1.userId)";
My question is, is there a way of returning the value of MAX(t2.DateCol)?
I tired this but is didn't reconise the column
$sql = "SELECT t1.*, MAX(t2.DateCol) ....
FYI: This sql is parsed into a custom PDO function so no need to warn about insecurities of mysql functions.

A quick solution (not knowing your skillset or data setup) - Move the sub-query in scope.
$sql = "SELECT t1.*,
(SELECT MAX(t2.DateCol)
FROM table2 t2
WHERE t2.userId= t1.userId) AS LastLogin
FROM table1 t1
HAVING '2016-08-01' > LastLogin ";
You will need to use HAVING instead of WHERE because you are comparing using an alias. If your query cannot use HAVING due to other factors then you'll need to repeat the subquery (not ideal).

If you want to show the aggregation result, you should select from the aggregation. In order to do so group your aggregation by userid. You can use HAVING to only select desired ones.
select t1.*, t2.max_datecol
from table1 t1
join
(
select userid, max(datecol) as max_datecol
from table2
group by userid
having max(datecol) < date '2016-08-01'
) t2 on t2.userid = t1.userid;

Related

Create a new variable in SQL by groupby

I have 2 sql table as follows:
First table t1:
Second table t2:
I need to calculate the count of "Number" column based on "Name" column from t1 and merge it with t2.
I wrote following code. But it seems not working
select *
from (
select Name, count(Number) as count
from t1
group by Name ) as a
join ( select *
from t2 ) as b
on a.Name = b.Name;
Can any one figure out what is wrong ? Thank you very much
I think you want to use SUM() instead of COUNT().
Because SUM() sums some integers, while COUNT() counts number of occurencies.
And as also stated in the comments, multiple columns with same names will create conflicts, so you have to select the wanted columns explicit (that is usually a good idea anyway).
You could obtain your wanted endgoal by this query:
select
SUM(Number),
t1.Name,
(select val1 FROM t2 WHERE t2.Name = t1.Name LIMIT 1) as val1
FROM t1
GROUP BY t1.Name
Example in sqlfiddle: http://sqlfiddle.com/#!9/04dddf/7

mysql query where I get the count of rows value equal to a row of same table

Can I have a mysql query where I get the count of rows, value equal to a row of same table.
Please check my below query and Can I optimize it. It's taking very long time to load
SELECT CASE
WHEN
table1.issue_type = 'email'
THEN
(SELECT Count(tabl1a.id) FROM table1 AS table1a WHERE table1a.email = table1.email)
ELSE
(SELECT Count(table1a.id) FROM table1 AS table1a WHERE table1a.phone_day = table1.phone_day)
END
AS emails
FROM table1
Advice me please
The usual remedy for such correlated subqueries in a select clause is to refactor by instead joining to a subquery which performs the aggregation. In this case, there are actually two different aggregations which you are doing, so we can left join to two subqueries to perform the aggregations.
SELECT
t1.issue_type,
COALESCE(t2.email_cnt, t3.phone_cnt) AS emails
FROM table1 t1
LEFT JOIN
(
SELECT email, COUNT(*) AS email_cnt
FROM table1
GROUP BY email
) t2
ON t1.email = t2.email AND
t1.issue_type = 'email'
LEFT JOIN
(
SELECT phone_day, COUNT(*) AS phone_cnt
FROM table1
GROUP BY phone_day
) t3
ON t1.phone_day = t3.phone_day AND
t1.issue_type <> 'email'
The trick here to add a condition to the join criteria checking whether or not the issue type is email. This guarantees that each record in your table will join to one and only one subquery. We use COALESCE to choose the non NULL count, coming from either the email or phone day aggregation.

How to get the maximum and not duplicated data in my table in mysql?

I table data is like this:
id car_id create_time remark
6c3befd0201a4691 4539196f55b54523986535539ed7beef 2017-07-1 16:42:49 firstcar
769d85b323bb4a1c 4539196f55b54523986535539ed7beef 2017-07-18 16:42:49 secondcar
984660c4189e499 575d90e340d14cf1bef4349b7bb5de9a 2017-07-3 16:42:49 firstjeep
I want to get the newest data. It means if there have two same car_id, I want to get only one according the newest time. How to write?
I try to write this, but I find it may wrong. If the other record may have the same create_time? How to fix that?
SELECT * FROM t_decorate_car
WHERE create_time IN
(SELECT tmptime FROM
(SELECT MAX(create_time),tmptime,car_id
FROM decorate
GROUP BY car_id
) tmp
)
One canonical way to handle this is to join your table to a subquery which finds the latest record for each car_id. This subquery serves as a filter to remove the older records you don't want to see.
SELECT t1.*
FROM t_decorate_car t1
INNER JOIN
(
SELECT car_id, MAX(create_time) AS max_create_time
FROM t_decorate_car
GROUP BY car_id
) t2
ON t1.car_id = t2.car_id AND
t1.create_time = t2.max_create_time
By the way, if you want to continue down your current road, you can also solve this using a correlated subquery:
SELECT t1.*
FROM t_decorate_car t1
WHERE t1.create_time = (SELECT MAX(t2.create_time) FROM t_decorate_car t2
WHERE t2.car_id = t1.car_id)
You were on the right track but you never connected the subquery to the main query using the right WHERE clause.

UPDATE with nested query in PostgreSQL with unwanted results

Due to its geographic capabilities I'm migrating my database from MySQL to PostgreSQL/PostGIS, and SQL that used to be so trivial is now are becoming painfully slow to overcome.
In this case I use a nested query to obtain the results in two columns, having in 1st column an ID and in the 2nd a counting result and insert those results in table1.
EDIT: This is the original MySQL working code that I need to be working in PostgreSQL:
UPDATE table1 INNER JOIN (
SELECT id COUNT(*) AS cnt
FROM table2
GROUP BY id
) AS c ON c.id = table1.id
SET table1.cnt = c.cnt
The result is having all rows with the same counting result, that being the 1st counting result of the nested select.
In MySQL this would be solved easily.
How would this work in PostgreSQL?
Thank you!
UPDATE table1 dst
SET cnt = src.cnt
FROM (SELECT id, COUNT (*) AS cnt
FROM table2
GROUP BY id) as src
WHERE src.id = dst.id
;

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.