More useful GROUP BY stuff.
By admin on Oct 18, 2008 | In Daily Commute - Standard hints/tips | Send feedback »
Hi, I've not gone into hibernation (yet)! I've been busy working on SqlServer queries in my job. One useful clause I use there is "top" to limit how many rows your query brings back. Now of course we have an equivalent in Mysql, the "LIMIT" clause. This is very useful to impose order on the rows your query brings back; you don't just have the facility to restrict the query to the first x rows. You can use OFFSET to skip rows. For example if I want to bring back 5 rows from the table EMP, but not the 5 lowest values, I could use the following statement:
Code:
select sal from emp order by sal limit 5 offset 5; | |
+------+ | |
| sal | | |
+------+ | |
| 1250 | | |
| 1300 | | |
| 1500 | | |
| 1600 | | |
| 2450 | | |
+------+ | |
5 rows in set (0.01 sec) |
Now another useful point about the LIMIT clause is you can use it on GROUP BY queries. It makes sense really, as the LIMIT clause is working on the record rows that are returned for display. Imagine that you need to group by a certain field and sum on a key value. Using the GROUP BY and ORDER BY clauses together will bring back results in ascending or (if specified by DESC) order. However, it may be that large data sets will produce more rows than you're interested in. Using the LIMIT clause will restrict the number of rows you bring back:
Code:
mysql> select deptno, sum(sal) from emp | |
-> group by deptno | |
-> order by sum(sal) desc | |
-> limit 2; | |
+--------+----------+ | |
| deptno | sum(sal) | | |
+--------+----------+ | |
| 20 | 10875 | | |
| 30 | 10650 | | |
+--------+----------+ |
I think the combination of GROUP BY with other functions and clauses, is what really makes sql powerful in analyzing data-sets. The following code demonstrates this, as it relies on the use of the UNION operator (remember to use the same number of columns and column data types in each select statement) to display different summaries in the same report:
Code:
mysql> select deptno, job, | |
-> 'TOTAL BY DEPT AND JOB' as category, | |
-> sum(sal) as sal | |
-> from emp | |
-> group by deptno, job | |
-> UNION ALL | |
-> select deptno, null, | |
-> 'TOTAL BY DEPT', sum(sal) | |
-> from emp | |
-> group by deptno | |
-> UNION ALL | |
-> select null, null, | |
-> 'GRAND TOTAL FOR TABLE', sum(sal) | |
-> from emp; | |
+--------+-----------+-----------------------+-------+ | |
| deptno | job | category | sal | | |
+--------+-----------+-----------------------+-------+ | |
| 10 | CLERK | TOTAL BY DEPT AND JOB | 1300 | | |
| 10 | MANAGER | TOTAL BY DEPT AND JOB | 2450 | | |
| 10 | PRESIDENT | TOTAL BY DEPT AND JOB | 5000 | | |
| 20 | ANALYST | TOTAL BY DEPT AND JOB | 6000 | | |
| 20 | CLERK | TOTAL BY DEPT AND JOB | 1900 | | |
| 20 | MANAGER | TOTAL BY DEPT AND JOB | 2975 | | |
| 30 | CLERK | TOTAL BY DEPT AND JOB | 950 | | |
| 30 | MANAGER | TOTAL BY DEPT AND JOB | 2850 | | |
| 30 | SALESMAN | TOTAL BY DEPT AND JOB | 6850 | | |
| 10 | NULL | TOTAL BY DEPT | 8750 | | |
| 20 | NULL | TOTAL BY DEPT | 10875 | | |
| 30 | NULL | TOTAL BY DEPT | 10650 | | |
| NULL | NULL | GRAND TOTAL FOR TABLE | 30275 | | |
+--------+-----------+-----------------------+-------+ | |
13 rows in set (0.54 sec) |
Pretty useful, hey? Before I leave, I have to admit to getting most of my code examples this time from the book SQL CookBook, published by O'Reilly. This really is a book worth purchasing, even if you're just starting in SQL or Mysql programming. You can find the book at this site booklink
Plug over - I wish you a profitable time working with your database of choice, and I hope you visit my site again soon.
Beautiful code
By admin on Aug 9, 2008 | In Daily Commute - Standard hints/tips, Formula 1 - More advanced stuff | Send feedback »
A bit of a delay since my last entry. I've started a new job which uses SQLServer (boo!) so my use of Mysql has been very small lately.
Anyway, let's take a little look at the group by clause. One of the most common ways I use it is to bring back records that are duplicated more than once. For example:
Code:
mysql> select ename, job, deptno,count(*) from emp group by ename, job, deptno having count(*) > 1 |
;
+-------+----------+--------+----------+
| ename | job | deptno | count(*) |
+-------+----------+--------+----------+
| WARD | SALESMAN | 30 | 2 |
+-------+----------+--------+----------+
1 row in set (0.00 sec)
Now how would you bring back (to delete or update) the latest insert of this duplicated record? In this case there are 2 records, one with empno of 7521, the latest record has the empno 8000. The following code (in my humble opinion) is very elegant, and is perhaps my favourite bit of sql I've seen.. thanks to this book sqlcookbook
Code:
mysql> select * from emp | |
-> where empno not in (select min(empno) | |
-> from emp | |
-> group by ename, job, deptno); |
+-------+-------+----------+------+------------+------+------+--------+
| empno | ename | job | mgr | hiredate | sal | comm | deptno |
+-------+-------+----------+------+------------+------+------+--------+
| 8000 | WARD | SALESMAN | 7698 | 1981-02-22 | 1250 | 500 | 30 |
+-------+-------+----------+------+------------+------+------+--------+
1 row in set (0.01 sec)
Here's another way of using the group by clause (again using subqueries) to bring back the employees with the highest and lowest salary in the dataset.
Code:
mysql> select ename, job, deptno | |
-> from emp | |
-> where sal in ((select min(sal) from emp), | |
-> (select max(sal) from emp)); |
+-------+-----------+--------+
| ename | job | deptno |
+-------+-----------+--------+
| SMITH | CLERK | 20 |
| KING | PRESIDENT | 10 |
+-------+-----------+--------+
2 rows in set (0.10 sec)
Now that, to me is also beautiful code.
Enjoy your coding - Until next time!
Getting Innodb to work
By admin on Jun 19, 2008 | In Formula 1 - More advanced stuff, Highway Maintenance - DBA stuff | Send feedback »
In order to enforce referential integrity and set up foreign keys, you need to convert your tables to using the Innodb storage engine (or create them using this to begin with on the storage engine). The syntax to convert a table to this storage is very straightforward:
ALTER TABLE orders ENGINE=INNODB;
However when I tried to do this, warnings were created. When I used "SHOW WARNINGS;" Mysql informed me that the tables were still using the MyISAM engine. Also, when I used the SHOW ENGINES command, I found out that the Innodb engine was disabled.
In order to enable Innodb (which is often disabled by default on Apache set-ups) the standard advice is to edit my.cnf file. The first part is to Comment out the skip-innodb line by using #skip-innodb
Also it's supposed to help by making sure the following lines are present:
Code:
innodb_data_home_dir = /opt/lampp/var/mysql/ | |
innodb_data_file_path = ibdata1:10M:autoextend | |
innodb_log_group_home_dir = /opt/lampp/var/mysql/ | |
innodb_log_arch_dir = /opt/lampp/var/mysql/ | |
# You can set .._buffer_pool_size up to 50 - 80 % | |
# of RAM but beware of setting memory usage too high | |
innodb_buffer_pool_size = 16M | |
innodb_additional_mem_pool_size = 2M | |
# Set .._log_file_size to 25 % of buffer pool size | |
innodb_log_file_size = 5M | |
innodb_log_buffer_size = 8M | |
innodb_flush_log_at_trx_commit = 1 | |
innodb_lock_wait_timeout = 50 |
However there is often more than one instance of this file my.cnf. Mine were in the directories /etc/mysql/ (often its in the /etc directory) and also in /var/lib/mysql/ Be warned - its exact location can differ on different set-ups so be sure to find the global version and the local one. Read this for further guidance:
mysql manual
Do this, restart your Apache and Mysql server, and you should be able to proceed.
However in my case, on looking at SHOW ENGINES, it seemed as though Innodb was still disabled. After much head-scratching and internet surfing I found out that if you have a zombie mysql process in the background, then Mysql never fully gets restarted. I should have realised this from the warning message "Another MySQL daemon is already running" I kept receiving. You can detect this on Unix based systems by performing this command:
ps -ef | grep mysql
Luckily, the problem was diagnosed here:
forum link
The solution turned out to be fairly straight-forward - Kill the zombie process, and then restart Apache and Mysql. You also have to put the correct path to your mysql.sock in your my.cnf files (see link again).
After doing all that I was successful - I can now convert tables to the Innodb engine.