I have a table called log_payment that has a series of payment records like:
log_user_id, log_date, log_payment_id
13, 2013-01-01 01:13:00, TRIAL<BR>
13, 2013-01-02 01:18:00, 1<BR>
13, 2013-01-03 01:05:00, 2
What I want to get is the payment id and date of the users last record. So I want that user_id's last transaction was 01/03 and has a payment id of 2. So I wrote this query:
select max(log_date) as max_date,log_user_id,log_payment_id from log_payment group by log_user_id
but it returns 13, 2013-01-03 01:05:00, TRIAL
So based on some data I found somewhere else, I tried this:
select log_user_id, max_date, log_payment_id from (select log_user_id,max(log_date) as max_date from log_payment group by _log_user_id) payment_table inner join log_payment on payment_table.log_user_id = log_payment.log_user_id and payment_table.max_date = log_payment.log_date
But this goes on for several minutes until I finally just cancel it. What am I missing?
Your query, which I have reparsed, looks good, except for the _log_user_id in the group by. It should be log_user_id:
select log_user_id,
max_date,
log_payment_id from
(select log_user_id,max(log_date) as max_date from log_payment group by _log_user_id)
payment_table
inner join
log_payment
on payment_table.log_user_id = log_payment.log_user_id and
payment_table.max_date = log_payment.log_date
Depending on the size of your tables the query might be slow. Try adding a LIMIT 10 at the end of the query to see if that gets you the desired result for the first 10 tuples.
--dmg
The best solution for the Group by order is use a subquery to make the order by for you:
SELECT t1.*
FROM `log_payment` t1
WHERE `id` = (
SELECT `id`
FROM `log_payment` `t2`
WHERE `t2`.`log_user_id` = `t1`.`log_user_id`
ORDER BY `t2`.`log_date` DESC
LIMIT 1
)
It also should be really fast. Of course it always relies on your index's setup.
Related
I have the following table (see pic.):
I need to sum(AT_amount) and group by AT_balance_tax_id .
But at the same time I need to get all AT_balance_tax_type_id for all groupped rows, of course without name duplications.
How to do that, I tried:
SELECT t.*
, i.*
, ABS(SUM(AT_amount)) amount
FROM account_transactions t
JOIN balance_tax_invoices i
ON i.id = t.AT_balance_tax_id
WHERE AT_createuser = 15
GROUP
BY AT_balance_tax_id
, AT_balance_tax_type_id
ORDER
BY AT_transactiondatetime DESC
It returns me not all AT_balance_tax_type_id for groupped rows.
Result is:
I expect this data for AT_balance_tax_id:
AT_amount AT_balance_tax_id AT_balance_tax_type_id
33000 9 1, 1, 3, 3
Delete duplicates:
AT_amount AT_balance_tax_id AT_balance_tax_type_id
33000 9 1, 3
Dont use * in the select clause. MySQL group by clause automatically includes other columns listed in select cause but not in Group By clause.
Also it seems you need GROUP_CONCAT function with distinct caluse. You can modify your query to -
SELECT at.`AT_balance_tax_id`
,GROUP_CONCAT(DISTINCT at.`AT_balance_tax_type_id`)
,ABS(SUM(AT_amount)) AS amount
FROM `account_transactions` at
INNER JOIN `balance_tax_invoices` bi ON bi.`id` = at.`AT_balance_tax_id`
WHERE `AT_createuser` = 15
GROUP BY at.`AT_balance_tax_id`
ORDER BY at.`AT_transactiondatetime` DESC
Also I have used aliases to increase the readability of query.
I have a database with over 100,000 records. I'm trying to get all customers who ordered only once searching by customer's email field (OrderEmail).
The SQL query is running for 10 minutes and then times out.
If I use short date ranges, I can get results but it still takes over 3 minutes.
How can I optimize the syntax to get it work?
SELECT
tblOrders.OrderID,
tblOrders.OrderName,
tblOrders.OrderEmail,
tblOrders.OrderPhone,
tblOrders.OrderCountry,
tblOrders.OrderDate
FROM
tblOrders
LEFT JOIN tblOrders AS orders_join ON orders_join.OrderEmail = tblOrders.OrderEmail
AND NOT orders_join.OrderID = tblOrders.OrderID
WHERE
orders_join.OrderID IS NULL
AND (tblOrders.OrderDate BETWEEN '2015-01-01' AND '2017-03-01')
AND tblOrders.OrderDelivered = - 1
ORDER BY
tblOrders.OrderID ASC;
I would expect the below to work - but I can't test it as you don't provide sample data. Well, I added a temporary table definition that could be used for the query ....
But , if you could actually change the data model to use an INTEGER id for the entity who placed the order (instead of a VARCHAR() email address), you would get considerably faster.
CREATE TEMPORARY TABLE IF NOT EXISTS
tblorders(orderid,ordername,orderemail,orderphone,ordercountry,orderdate) AS (
SELECT 1,'ORD01','adent#hog.com' ,'9-991' ,'UK', DATE '2017-01-01'
UNION ALL SELECT 2,'ORD02','tricia#hog.com','9-992' ,'UK', DATE '2017-01-02'
UNION ALL SELECT 3,'ORD03','ford#hog.com' ,'9-993' ,'UK', DATE '2017-01-03'
UNION ALL SELECT 4,'ORD04','zaphod#hog.com','9-9943','UK', DATE '2017-01-04'
UNION ALL SELECT 5,'ORD05','marvin#hog.com','9-9942','UK', DATE '2017-01-05'
UNION ALL SELECT 6,'ORD06','ford#hog.com' ,'9-993' ,'UK', DATE '2017-01-06'
UNION ALL SELECT 7,'ORD07','tricia#hog.com','9-992' ,'UK', DATE '2017-01-07'
UNION ALL SELECT 8,'ORD08','benji#hog.com' ,'9-995' ,'UK', DATE '2017-01-08'
UNION ALL SELECT 9,'ORD09','benji#hog.com' ,'9-995' ,'UK', DATE '2017-01-09'
UNION ALL SELECT 10,'ORD10','ford#hog.com' ,'9-993' ,'UK', DATE '2017-01-10'
)
;
SELECT
tblOrders.OrderID
, tblOrders.OrderName
, tblOrders.OrderEmail
, tblOrders.OrderPhone
, tblOrders.OrderCountry
, tblOrders.OrderDate
FROM tblOrders
JOIN (
SELECT
OrderEmail
FROM tblOrders
GROUP BY
OrderEmail
HAVING COUNT(*) = 1
) singleOrders
ON singleOrders.OrderEmail = tblOrders.OrderEmail
ORDER BY OrderID
;
OrderID|OrderName|OrderEmail |OrderPhone|OrderCountry|OrderDate
1|ORD01 |adent#hog.com |9-991 |UK |2017-01-01
4|ORD04 |zaphod#hog.com|9-9943 |UK |2017-01-04
5|ORD05 |marvin#hog.com|9-9942 |UK |2017-01-05
As you can see, it returns Mr. Dent, Zaphod and Marvin, who all occur only once in the example data.
Another approach that might work is that you group by email address and get only those with one entry. It may behave unpredictably if you want to get customers with multiple orders but it should be fine for this particular case:
SELECT
tblOrders.OrderID,
tblOrders.OrderName,
tblOrders.OrderEmail,
tblOrders.OrderPhone,
tblOrders.OrderCountry,
tblOrders.OrderDate,
count(tblOrders.OrderID) as OrderCount
FROM
tblOrders
WHERE
tblOrders.OrderDate BETWEEN '2015-01-01' AND '2017-03-01'
AND tblOrders.OrderDelivered = - 1
GROUP BY
tblOrders.OrderEmail
HAVING
OrderCount = 1
ORDER BY
tblOrders.OrderID ASC;
Also, I suspect that if you're seeing so long query times with just 100k records, you probably don't have an index on the OrderEmail column - I suggest setting that up and that might help with your original queries as well.
This does not work in Oracle, or SQL Server but it does work in MySQL and SQLite. So, while the code is not portable between different RDBMS, it works for this particular case.
Given the schema
The following query
SELECT a.user_id,
a.id,
a.date_created,
avg(ai.level) level
FROM assessment a
JOIN assessment_item ai ON a.id = ai.assessment_id
GROUP BY a.user_id, a.id;
Returns these results
user_id, a.id, a.date_created, level
1, 99, "2015-07-13 18:26:00", 4.0000
1, 98, "2015-07-13 19:04:58", 6.0000
13, 9, "2015-07-13 18:26:00", 2.0000
13, 11, "2015-07-13 19:04:58", 3.0000
I would like to change the query such that only the earliest results is returned for each user. In other words, the following should be returned instead
user_id, a.id, a.date_created, level
1, 99, "2015-07-13 18:26:00", 4.0000
13, 9, "2015-07-13 18:26:00", 2.0000
I think I need to add a HAVING clause, but I'm struggling to figure out the exact syntax.
I have done something like this, except for a small difference I wanted first 5 per group. The usage case was for reporting - means time for running query / creation of temp table was not a constraint.
The solution I had:
Create a new table with columns as id( a reference to original table) and id can be unique/primary
INSERT IGNORE INTO tbl1 (id) select min(id) from original_tbl where id not in (select id from tbl1) group by user_id
Repeat step 2 as many times you required( in my case it was 5 times). the new table table will have only the ids you want to show
Now run a join on tbl1 and original table will give you the required result
Note: This might not be the best solution, but this worked for me when I had to share the report in 2-3hours in a weekend. And the data size I had was around 1M records
Disclaimer: I am in a bit of a hurry, and have not tested this fully
-- Create a CTE that holds the first and last date for each user_id.
with first_and_last as (
-- Get the first date (min) for each user_id
select a.[user_id], min(a.date_created) as date_created
from assessment as a
group by a.[user_id]
-- Combine the first and last, so each user_id should have two entries, even if they are the same one.
union all
-- Get the last date (max) for each user_id
select a.[user_id], max(a.date_created)
from assessment as a
group by a.[user_id]
)
select a.[user_id],
a.id,
a.date_created,
avg(ai.[level]) as [level]
from assessment as a
inner join assessment_item as ai on a.id = ai.assessment_id
-- Join with the CTE to only keep records that have either the min or max date_created for each user_id.
inner join first_and_last as fnl on a.[user_id] = fnl.[user_id] and a.date_created = fnl.date_created
group by a.[user_id], a.id, a.date_created;
i want to select something from table while one condition is true,
SELECT * FROM (SELECT * FROM`table1` `t1` ORDER BY t1.date) `t2` WHILE t2.id!=5
when while condition comes to false it stop selecting next rows.
Please help me, I have already search a lot and many similars in stackoverflow but I can't get it.
please don't tell me about where , i want solution in sql not in php or anything other
OK the real problem is here
SELECT *,(SELECT SUM(t2.amount) FROM (select * from transaction as t1 order by t1.date) `t2`) as total_per_transition FROM transaction
here i want to calculate total balance on each transaction
First find the first date where the condition fails, so where id=5:
SELECT date
FROM table1
WHERE id = 5
ORDER BY date
LIMIT 1
Then make the above a derived table (we call it lim) and join it to the original table to get all rows with previous dates: t.date < lim.date
SELECT t.*
FROM table1 AS t
JOIN
( SELECT date
FROM table1
WHERE id = 5
ORDER BY date
LIMIT 1
) AS lim
ON t.date < COALESCE(lim.date, '9999-12-31') ;
The COALESCE() is for the case when there are no rows at all with id=5 - and in that case we want all rows from the table.
I am trying to write a query which will give me the last entry of each month in a table called transactions. I believe I am halfway there as I have the following query which groups all the entries by month then selects the highest id in each group which is the last entry for each month.
SELECT max(id),
EXTRACT(YEAR_MONTH FROM date) as yyyymm
FROM transactions
GROUP BY yyyymm
Gives the correct results
id yyyymm
100 201006
105 201007
111 201008
118 201009
120 201010
I don’t know how to then run a query on the same table but select the balance column where it matches the id from the first query to give results
id balance date
120 10000 2010-10-08
118 11000 2010-09-29
I've tried subqueries and looked at joins but i'm not sure how to go about using them.
You can make your first select an inline view, and then join to it. Something like this (not tested, but should give you the idea):
SELECT x.id
, t.balance
, t.date
FROM your_table t
/* here, we make your select an inline view, then we can join to it */
, (SELECT max(id) id,
EXTRACT(YEAR_MONTH FROM date) as yyyymm
FROM transactions
GROUP BY yyyymm) x
WHERE t.id = x.id