$sql = "SELECT EXISTS (SELECT usernev AS juzernev, SUM(mbsent) AS summ FROM data WHERE datum > DATE_ADD(NOW(), INTERVAL -1 MONTH) AND usernev='csib')'";
I have this query on this table(data) - the first col. is the usernev:
| sajtos | 1323 | 411 | 193.225.249.2 | 10.8.0.10 | 3661 | 2015-03-19 17:25:37 | 87 |
| csib | 318 | 26 | 5.187.169.135 | 10.8.0.6 | 10849 | 2015-03-19 19:11:37 | 88 |
| csib | 5 | 1 | 5.187.169.135 | 10.8.0.6 | 1234 | 2015-03-19 22:50:23 | 89 |
| gyuri | 26 | 31 | 193.225.249.2 | 10.8.0.14 | 3001 | 2015-03-19 22:56:54 | 90 |
So the problem is when i use this query it is returns 1 -> So with this query with username csib there were good results. But when i change the usernev to another (which is not exists) it is also returns 1.
Am i doing something wrong or i cant do it with EXISTS?
An aggregation function with no group by always returns one row. And, a query that has one row satisfies exists.
As a general rule, I always use select 1 with exists. There is no need to select anything more:
SELECT EXISTS (SELECT 1
FROM data
WHERE datum > DATE_ADD(NOW(), INTERVAL -1 MONTH) AND
usernev = 'csib'
);
You can also write this query as:
SELECT COALESCE(MAX(1), 0)
FROM data
WHERE datum > DATE_ADD(NOW(), INTERVAL -1 MONTH) AND
usernev = 'csib';
With an index on data(usernev, datum), the two probably have similar performance. Otherwise, the first would have better performance. I just mention this version because it has an aggregation function, making use of the fact that an aggregation query with no group by always returns one row.
The reason is you are using aggregate function sum and this will always return one row weather or not if there is any data.
You may need to remove the aggregate function inside the exists
Here is a demo
mysql> select exists (select sum(amount) from paymentlog where idusers = 1 );
+----------------------------------------------------------------+
| exists (select sum(amount) from paymentlog where idusers = 1 ) |
+----------------------------------------------------------------+
| 1 |
+----------------------------------------------------------------+
1 row in set (0.00 sec)
mysql> select exists (select sum(amount) from paymentlog where idusers = 1111111111 );
+-------------------------------------------------------------------------+
| exists (select sum(amount) from paymentlog where idusers = 1111111111 ) |
+-------------------------------------------------------------------------+
| 1 |
+-------------------------------------------------------------------------+
1 row in set (0.00 sec)
mysql> select exists (select * from paymentlog where idusers = 1111111111 );
+---------------------------------------------------------------+
| exists (select * from paymentlog where idusers = 1111111111 ) |
+---------------------------------------------------------------+
| 0 |
+---------------------------------------------------------------+
1 row in set (0.00 sec)
Related
I have a table that contains too many records and each bunch of records belong to someone:
---------------------
id | data | username
---------------------
1 | 10 | ali
2 | 11 | ali
3 | 12 | ali
4 | 20 | omid
5 | 21 | omid
6 | 30 | reza
now I want to create a query to result me like this:
1-10-ali
4-20-omid
6-30-reza
2-11-ali
5-21-omid
3-12-ali
Is there anyway to create a query to result me one record per each username and then one from another, and another to the end?
Unfortunately MySQL doesn't have a ranking system so you can use UDV (user defined variables) to rank your records like so.
SELECT id, `data`, name
FROM
( SELECT
id, `data`, name,
#rank := if(#name = name, #rank + 1, 1) as rank,
#name := name
FROM test
CROSS JOIN (SELECT #rank := 1, #name := '') temp
ORDER BY name, `data`
) t
ORDER BY t.rank, t.name, t.data
Sql Fiddle to play with
Output:
+---------------------+
| id | data | name |
+-----+------+--------+
| 1 | 10 | ali |
+---------------------+
| 4 | 20 | omid |
+---------------------+
| 6 | 30 | reza |
+---------------------+
| 2 | 11 | ali |
+---------------------+
| 5 | 21 | omid |
+---------------------+
| 3 | 12 | ali |
+---------------------+
The classic SQL approach is a self join and grouping that lets you determine a row's ranking position by counting the number of rows that come before it. As this is probably slower I doubt I could talk you out of the proprietary method but I mention it to give you an alternative.
select t.id, min(t.`data`), min(t.username)
from test t inner join test t2
on t2.username = t.username and t2.id <= t.id
group by t.id
order by count(*), min(t.username)
Your example would work with
SELECT id, `data`, name
FROM tbl
ORDER BY `data` % 10,
username
`data`;
If data and username do not have the desired pattern, then improve on the example.
This may be a little confusing but please bear with me. Here's the thing:
I have a database that contains ~1000 records, as the following table illustrates:
+------+----------+----------+
| id | date | amount |
+------+----------+----------+
| 0001 | 14/01/15 | 100 |
+------+----------+----------+
| 0002 | 14/02/04 | 358 |
+------+----------+----------+
| 0003 | 14/05/08 | 1125 |
+------+----------+----------+
What I want to do is this:
Retrieve all the records beginning at 2014 and until yesterday:
WHERE `date` > '14-01-01' AND `date` < CURDATE()
But also get the sum of amount up to the current date, this is:
WHERE `date` < CURDATE()
I've already got this working by just selecting all the records based on the second condition, getting the sum, and then excluding those which don't match the first condition. Something like this:
SELECT `id`, `date`, `amount` FROM `table`
WHERE `date` < CURDATE()
And then:
$rows = fetchAll($PDOStatement);
foreach($rows as $row) {
$sum += $row->amount;
if (
strtotime($row->date) > strtotime('14-01-01') &&
strtotime($row->date) < strtotime(date('Y-m-d'))
) {
$valid_rows[] = $row;
}
}
unset $rows;
Is there a way to achieve this in a single query, efficiently? Would a transaction be more efficient than sorting out the records in PHP? This has to be SQL-standard compliant (I'll be doing this on MySQL and SQLite).
Update:
It doesn't matter if the result ends up being something like this:
+------+----------+----------+-----+
| id | date | amount | sum |
+------+----------+----------+-----+
| 0001 | 14/01/15 | 100 | 458 |
+------+----------+----------+-----+
| 0002 | 14/02/04 | 358 | 458 |
+------+----------+----------+-----+
| 0003 | 14/05/08 | 1125 | 458 |
+------+----------+----------+-----+
The worst case would be when the resulting set ends up being the same as the set that gives the sum (in this case appending the sum would be irrelevant and would cause an overhead), but for any other regular cases the bandwith save would be huge.
You can create a special record with your sum and add it at the end of your first query
SELECT * FROM `table` WHERE `date` > '14-01-01' AND `date` < CURDATE()
UNION
SELECT 9999, CURDATE(), SUM(`amount`) FROM `table` WHERE `date` < CURDATE()
Then you will have all your desired record and the record with id 9999 or whatever is your sum
This could be achieved by correlated subquery, something like below:
SELECT *, (SELECT SUM(amount) FROM t WHERE t.date < t1.date) AS PrevAmount
FROM t AS t1
WHERE `date` > '14-01-01' AND `date` < CURDATE()
However it is very unefficient if the number of records is large.
It's hackish, but:
> select * from foo;
+------+------+
| id | val |
+------+------+
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
| 4 | 4 |
| 5 | 5 |
+------+------+
5 rows in set (0.02 sec)
> select * from foo
left join (
select sum(val)
from foo
where id < 3
) AS bar ON 1=1
where id < 4;
+------+------+----------+
| id | val | sum(val) |
+------+------+----------+
| 1 | 1 | 3 |
| 2 | 2 | 3 |
| 3 | 3 | 3 |
+------+------+----------+
Basically, do your summing in a joined subquery. That'll attach the sum result to every row in the outer table's results. You'll waste a bit of bandwidth sending that duplicated value out with every row, but it does get you the results in a "single" query.
EDIT:
You can get the SUM using a LEFT OUTER JOIN.
SELECT t1.`id`, t1.`date`, t2.sum_amount
FROM
`table` t1
LEFT OUTER JOIN
(
SELECT SUM(`amount`) sum_amount
FROM `table`
WHERE `date` < CURDATE()
) t2
ON 1 = 1
WHERE t1.`date` > STR_TO_DATE('01,1,2014','%d,%m,%Y') AND t1.`date` < CURDATE();
This will do what you want it to do...optimizing the subquery is the real challenge:
SELECT id,date,amount,(SELECT SUM(amount) FROM table) AS total_amount
FROM table
WHERE date BETWEEN '14-01-01' AND DATE_ADD(CURDATE(), INTERVAL -1 DAY)
I have two table tbl_issue_log, tbl_magazine_issue
tbl_issue_log
============
+----------+--------+--------+-----------+---------------------+
| issue_id | mag_id | log_id | operation | updated_time |
+----------+--------+--------+-----------+---------------------+
| 2 | 1 | 1 | 1 | 2014-01-30 21:29:44 |
| 3 | 4 | 1 | 1 | 2015-01-30 21:29:44 |
| 2 | 1 | 1 | 3 | 2015-01-31 21:29:44 |
+----------+--------+--------+-----------+---------------------+
tbl_magazine_issue
=================
+----------+-------------+-------------+------------------+------------+------------+-------------------+---------------+
| ISSUE_ID | ISSUE_NAME | MAGAZINE_ID | COVER_PAGE_THUMB | FROM_DATE | TO_DATE | issue_description | login_page_no |
+----------+-------------+-------------+------------------+------------+------------+-------------------+---------------+
| 2 | test issue | 1 | cover page | 2014-01-30 | 2015-01-30 | sdssdg fsdf | 20 |
| 3 | test issue1 | 4 | cover page1 | 2014-01-30 | 2015-01-30 | sdssdg fsdf | 20 |
+----------+-------------+-------------+------------------+------------+------------+-------------------+---------------+
in tbl_issue_log contain multiple records for same issue id. i want only one issue at a time
and this must latest updated time.
My query is this
SELECT
`tbl_issue_log`.`operation`,
`tbl_magazine_issue`.`ISSUE_ID`,
`tbl_magazine_issue`.`ISSUE_NAME`,
`tbl_magazine_issue`.`MAGAZINE_ID`,
`tbl_magazine_issue`.`COVER_PAGE_THUMB`,
`tbl_magazine_issue`.`FROM_DATE`,
`tbl_magazine_issue`.`TO_DATE`,
`tbl_magazine_issue`.`issue_description`,
`tbl_magazine_issue`.`login_page_no`
FROM
`tbl_issue_log`
LEFT JOIN
`tbl_magazine_issue` ON tbl_magazine_issue.ISSUE_ID = tbl_issue_log.issue_id
WHERE
(tbl_issue_log.mag_id = '1')
AND (tbl_magazine_issue.ISSUE_STATUS = 3)
AND (tbl_issue_log.updated_time > '2014-02-25 00:42:22')
GROUP BY tbl_issue_log.issue_id
ORDER BY tbl_issue_log updated_time DESC;
Here i got issue id based output . But not getting the latest updated timeed record.
If any one about this please help me.
Try this
SELECT * FROM
(SELECT
`tbl_issue_log`.`operation`,
`tbl_magazine_issue`.`ISSUE_ID`,
`tbl_magazine_issue`.`ISSUE_NAME`,
`tbl_magazine_issue`.`MAGAZINE_ID`,
`tbl_magazine_issue`.`COVER_PAGE_THUMB`,
`tbl_magazine_issue`.`FROM_DATE`,
`tbl_magazine_issue`.`TO_DATE`,
`tbl_magazine_issue`.`issue_description`,
`tbl_magazine_issue`.`login_page_no`
FROM
`tbl_issue_log`
LEFT JOIN
`tbl_magazine_issue` ON tbl_magazine_issue.ISSUE_ID = tbl_issue_log.issue_id
WHERE
(tbl_issue_log.mag_id = '1')
AND (tbl_magazine_issue.ISSUE_STATUS = 3)
AND (tbl_issue_log.updated_time > '2014-02-25 00:42:22')
ORDER BY tbl_issue_log.updated_time DESC ) TEMP_TABLE
GROUP BY ISSUE_ID
Can you try changing order by clause as
ORDER BY tbl_issue_log.updated_time DESC;
Edit ---
As you are grouping on issue_id, mysql will select first row that matches the issue_id. The order by runs later which essentially does not return what you are looking for. You may need to use a subquery approach for this.
select some_table.* FROM
(
SELECT
MAX(tbl_issue_log.updated_time) AS updated_time,
`tbl_issue_log`.`operation`,
`tbl_magazine_issue`.`ISSUE_ID`,
`tbl_magazine_issue`.`ISSUE_NAME`,
`tbl_magazine_issue`.`MAGAZINE_ID`,
`tbl_magazine_issue`.`COVER_PAGE_THUMB`,
`tbl_magazine_issue`.`FROM_DATE`,
`tbl_magazine_issue`.`TO_DATE`,
`tbl_magazine_issue`.`issue_description`,
`tbl_magazine_issue`.`login_page_no`
FROM
`tbl_issue_log`
LEFT JOIN
`tbl_magazine_issue` ON tbl_magazine_issue.ISSUE_ID = tbl_issue_log.issue_id
WHERE
(tbl_issue_log.mag_id = '1')
AND (tbl_magazine_issue.ISSUE_STATUS = 3)
AND (tbl_issue_log.updated_time > '2014-02-25 00:42:22')
GROUP BY tbl_issue_log.issue_id
) some_table
ORDER BY some_table.updated_time DESC;
Okay guys I have big problem with mySQL. I need to to do select that gets the free tables in the restaurant in specific time. Here are my tables:
tables:
+-------------+
| id | chairs|
+-------------+
| 1 | 3 |
| 2 | 5 |
| 4 | 10 |
| 7 | 12 |
| 10 | 6 |
+-------------+
and reservations:
+---------------------------------+
| id | table_id | start_datetime |
+----------------|---------------------+
| 1 | 3 | 2013-11-27 16:15:00 |
| 2 | 5 | 2013-11-27 19:00:00 |
+----------------|---------------------+
I try something like this
SELECT *
FROM wp_reserveit_tables,wp_reserveit_reservations
WHERE (wp_reserveit_tables.chairs = wp_reserveit_reservations.table_id
AND wp_reserveit_reservations.start_datetime - INTERVAL 1 HOUR >= '2013-11-29 16:15:00'
AND wp_reserveit_reservations.start_datetime + INTERVAL 1 HOUR <= '2013-11-29 16:15:00')
OR wp_reserveit_tables.chairs <> wp_reserveit_reservations.table_id
but it gives me SQL error.
So please if you have and idea please write it down.
Thanks
Reservation table will have entry only if there are any reservations. So we can do it simply with NOT IN clause I guess,
SELECT * FROM wp_reserveit_tables WHERE chairs_id NOT IN
(SELECT table_id FROM wp_reserveit_reservations GROUP BY table_id)
In your query you have joined them with chairs column with table_id column. So am not sure which one matches. So please alter the column in Where clause as per your requirement.
You have column name start_datetime in your query and your table structure specifies it to be starttime.
Unless you specify further, this does look like a valid error if it is not a typo.
I'm trying to build a ranking system in a mysql database.
I've found several tutorials on ranking and items here on StackOverflow about ranking individual rows against each other.
However, my issue is that I need to group rows by a user id column, add up the values to a second column grouped by user id, then rank them against other groups of a different user id.
Here's an example of the table I'm using:
user_id km_skied date_entered
1 34 2010-08-19
3 2 2010-08-23
1 3 2010-08-13
4 23 2010-08-01
3 5 2010-08-02
The result printout would be by rank:
Skier Rank:
Rank User ID Total KM
1 1 37
2 4 23
3 3 7
Also, I was wondering how I find the rank for a specific user. Meaning, if I know what the user id is, can I give them just their rank? Like say
"Your Rank: 2 of 345"
That is the second part of this.
Anyone know how to do that?
Thanks!
Troy
Your query should look something like this. Add the ranking logic to the outer loop.
select * from
(select user_id, sum(km_skied) as km from ski group by user_id) x
order by x.km desc;
Don't know if it's an option, but you can use a temporary table for rankings as follows:
create temporary table ranks (rank int primary key auto_increment, user_id int, km int);
insert into ranks (user_id, km)
select user_id, km from (
select user_id, sum(km_skied) as km from ski group by user_id
) x order by x.km desc;
This gives you what you want:
mysql> select * from ranks;
+------+---------+------+
| rank | user_id | km |
+------+---------+------+
| 1 | 1 | 37 |
| 2 | 4 | 23 |
| 3 | 3 | 7 |
+------+---------+------+
3 rows in set (0.00 sec)
One downside to this approach is that skiers who are tied won't get the same rank.
Do the grouping in subquery and ranking of the results (using any of the methods you've found before) in outer query.
Thanks for your help guys.
I was able to come up with an answer based on the following Query:
$totalQuery = "SELECT SUM(track_length) as usertracklength, username, MAX(track_create_time) as lasttrack, count(DISTINCT track_create_time) as totaldays FROM user_tracks GROUP BY username ORDER BY usertracklength DESC";
$totalResult = mysql_query($totalQuery);
$rankResult = mysql_query($totalQuery);
$totalNumEntries = mysql_num_rows($totalResult);
Then Ouputting that to an array
// rank position array
$rankArray = array();
while ($row1 = mysql_fetch_array($rankResult)) {
$rankArray[] = $row1['username'];
}
Then finding position of that username in the array by using a foreach in php
foreach ($rankArray as $rank => $user) {
if ($user == $username) {
$yourRank = $rank+1;
}
}
It's the long way around, but I suppose it works for what I'm going for.
Was kind of hoping to get it done within the mysql query for efficiency.
Thanks!
You could try grouping to sum the Km as a first query, then follow it by a correlated subquery to find the ranks. For instance, if your values are stored in a table called "test", sum the Km values into a table called testtbl and then do the ranking.
mysql> select * from test;
+------+--------+------+
| Id | km_run | name |
+------+--------+------+
| 1 | 34 | a |
| 3 | 2 | c |
| 1 | 3 | a |
| 4 | 23 | d |
| 3 | 5 | c |
+------+--------+------+
5 rows in set (0.00 sec)
mysql> create table testtbl as
(select Id, sum(km_run) as tot
from test
group by Id);
Query OK, 3 rows affected (0.04 sec)
Records: 3 Duplicates: 0 Warnings: 0
mysql> select * from testtbl;
+------+------+
| Id | tot |
+------+------+
| 1 | 37 |
| 3 | 7 |
| 4 | 23 |
+------+------+
3 rows in set (0.00 sec)
mysql> select t1.Id,t1.tot,
((select count(distinct t2.tot) from testtbl t2 where t1.tot < t2.tot)+1) as Rk from testtbl t1
order by Rk;
+------+------+------+
| Id | tot | Rk |
+------+------+------+
| 1 | 37 | 1 |
| 4 | 23 | 2 |
| 3 | 7 | 3 |
+------+------+------+
3 rows in set (0.00 sec)