UNION 명령의 일반적인 MySQL 오류
Incorrect usage of UNION and ORDER BY
이 문제를 해결하기 위해서는 select 문을 괄호(parenthesis) 로 감싸주어야 합니다.
(SELECT col1,col2,col3 FROM table1)
UNION
(SELECT cola,colb,colc FROM table2)
Incorrect usage of UNION and ORDER BY
(SELECT col1,col2,col3 FROM table1)
UNION
(SELECT cola,colb,colc FROM table2)
Max connections 에러가 발생하면 PHP페이지에 Too many connection가 나옵니다.
이는 mysql에서 동시열결가능한 클라이언트수를 넘었기 때문이다.
이런에러의 원인으로는 다음과 같습니다.
1) mysql_connect()함수로 DB connect를 했다면 해당스크립트가 종료됨과 동시에 mysql_close()함수를 호출하지않아도 자동으로 연결이 종료된다.
하지만, mysql_pconnect()함수는 해당스크립트가 종료된후 mysql_close()함수가 호출되었더라도 연결이 끊어지지않은채로 계속 연결을 유지하고 있어서 금방 커넥션을 다채울수도 있다.
2) MySQL 의 메뉴얼을 보면 mysql_connect()함수를 사용하면 해당스크립트의 종료와 함께 연결이 종료된다고 되어있지만 ./mysqladmin -u -p processlist라는 명령어를 통해서 살펴보면 그대로 살아 있음을 알수 있다.
맨위에서 살펴보았던 mysqladmin -u -p variables의 결과로서 볼수 있는 여러가지 시작옵션들중 wait_timeout 의 값만큼 서버에 그대로 연결을 유지한채로 남아 있는 것이다.
따라서, 이것이 too many connections의 직접적인 이유인 것이다.
[콘솔에서 커넥션수 알아내기]
$ mysqladmin -u root -p variables | grep max_connection
| max_connections | 100
[mysql에서 커넥션수 알아내기]
show variables like '%max%';
이제는, 클라이언트의 동시 접속자를 늘리는 명령이다.
먼저 mysql을 데몬을 kill 합니다.다시 아래와같이 서버를 재가동한다.
#mysqld_safe --language=korean --datadir=/data --user=mysql --default-character-set=euc_kr -O max_connections=256 -O table_cache=128 -O wait_timeout=60 &
#mysqld_safe -O max_connections=256 -O table_cache=128 -O wait_timeout=60 &
참고로, 리눅스나 솔라리스 계열에서는 클라이언트의 동시 접속자수가 500 ~ 1,000 까지 가능하다고 합니다.하지만 1000 설정 하려고 해도 4way cpu에 상당히 많은 램이 필요하다고 할고 있다.
256이 적당하다.
여기서 max_connections 는 mysql 에 접속 할 수 있는 최대 사용자를 가르키며...
wait_timeout 은 접속 후 연결을 지속시키는 시간을 말한다
이렇게 하신 후 다시 확인한다.
[콘솔에서]
$ mysqladmin -u root -p variables | grep max_connection
$ mysqladmin -u root -p variables | grep wait_timeout
[mysql에서]
mysql> show variables like '%max_connection%';
mysql> show variables like 'wait_timeout';
[커넥션 확인]
mysql> show full processlist
[출처] mysql-too many connection|작성자 요루짱
MySQL 설정 파일(my.cnf or my.ini) 에 직접 추가 한 다음 재실행을 하셔도 적용이 됩니다.
[mysqld]
max_connections = 256
table_cache = 128
wait_timeout = 60
우분투 서버의 경우 mysql 원격 접속을 하기 위해서
/etc/mysql/my.cnf 파일의 bind-address 부분은 주석처리해준다
#
# Instead of skip-networking the default is now to listen only on
# localhost which is more compatible and is not less secure.
#bind-address = 127.0.0.1
#
우분투를 설치시 mysql character set 은 latin1 으로 되어 있기 때문에 my.cnf 파일에
다음 항목을 추가 하거나 수정해준다. 빨간 부분은 추가된 부분, 파란색 부분은 수정한 부분
----------------------------------------------------------
[client]
port = 3306
socket = /var/run/mysqld/mysqld.sock
default-character-set = euckr
[mysqld]
language = /usr/share/mysql/korean
character-set-client-handshake=FALSE
init_connect="SET collation_connection = euckr_korean_ci"
init_connect="SET NAMES euckr"
default-character-set = euckr
character-set-server = euckr
collation-server = euckr_korean_ci
[mysqldump]
default-character-set = euckr
[mysql]
default-character-set = euckr
-----------------------------------------------
mysql> ALTER DATABASE [Database] DEFAULT CHARACTER SET utf8;
mysql> ALTER TABLE [Table] DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
# cd /usr/local/mysql/bin //mysql이 설치된 디렉토리의 bin
# killall mysqld
# ./safe_mysqld --skip-grant &
# mysql
mysql> use mysql;
mysql> update user set password=PASSWORD('패스워드') where user='root';
mysql> flush privileges;
mysql> quit;
# killall mysqld
# ./safe_mysqld -uroot &

전 부터 인수관련 기사들이 나오긴했었나 보네요..
인수관련 기사를 접해 보지 못한터라.... ^^
오늘 인수완료 되었다는 기사가 나왔다길래.. ㅎㅎ
Integer Type
- TINYINT 128부터 127 까지의 정수형 타입, 부호가 없는 정수 0∼255까지 지원Stirng Type
-CHAR(m) 0~255까지 입력이 가능하며, 길이가 m길이만큼 고정 사이즈가
된다.
-VARCHAR (m)0~255까지 입력은 가능하지만, 길이는
고정이 아닌 가변 사이즈이다.
-BINARY(m) 이진 데이터를 바이트 지정한 만큼 고정
길이
-VARBINARY(m) 이전 데이터를 바이트 지정한 만큼
넣는다. 가벼 길이
-TINYBLOB 이전 데이터를 최대 255자까지 입력이
가능하다
-BLOB
최대 길이가 63535인 문자를 저장
-TEXT
최대 길이가 63535인 문자를 저장
-MEDIUMBLOB
최대 길이가 16777215인 문자를 저장
-LONGBLOB 최대 입력이 4G 이진데이타, 최대 2^32
바이트
-LONGTEXT 최대 입력이 4G 1바이트 문자열, 최대
2^32 자
/* Point */
- BLOB타입은 대소문자를 구분하고 TEXT타입은 대소문자를 구분하지 않는점이 틀림
- MySQL 3.23.2이번 버전에서는 BLOB와 TEXT컬럼에는 인덱스를 만들수 없다
- BLOB와 TEXT컬럼의 저장시에 문자열 됫부분의 공백이 제거되지 않는다.
- BLOB와 TEXT컬럼은 DEFAULT를 지정할 수 없다.
Data Type
-DATE
날짜를 표현하는 유형 [YYYY-MM-DD], 1000-01-01 ∼ 9999-12-31까지 나타낼 수 있다
-DATETIME
날짜와 시간을 표현하는 유형 [YYYY-MM-DD HH:MM:SS], 1000-01-01 00:00:00 ∼ 9999-12-31 23:59:59
-TIMESTAMP
자동변경 컬럼 타입(4 Byte ), 1970-01-01 00:00:00부터 2037년 까지 표현
-TIME
시간을 표현하는 유형 [HH:MM:SS], 839:59:59 ∼ 833:59:59 까지 표현
-YEAR
년도를 표현하는 유형[기본적으로 4자리로 사용], 1901년 ∼ 2155년
출처 : http://www.xaprb.com/blog/2006/05/26/how-to-write-full-outer-join-in-mysql/
In this article I’ll show several ways to emulate a FULL OUTER join on a RDBMS
that doesn’t support it, as is the case with even the most recent
versions of MySQL. This useful query is surprisingly tricky to get
right.
A standard SQL FULL OUTER join is like a LEFT or RIGHT join, except that it includes all rows from both tables, matching them where possible and filling in with NULLs where there is no match. I’ll illustrate that for clarity. Here are two of my favorite tables, apples and oranges:
| Variety | Price |
|---|---|
| Fuji | 5.00 |
| Gala | 6.00 |
| Variety | Price |
|---|---|
| Valencia | 4.00 |
| Navel | 5.00 |
I’ll join them on price. Here is the left join:
select * from apples as a
left outer join oranges as o on a.price = o.price
| variety | price | variety | price |
|---|---|---|---|
| Fuji | 5 | Navel | 5 |
| Gala | 6 | NULL | NULL |
And the right join:
select * from apples as a
right outer join oranges as o on a.price = o.price
| variety | price | variety | price |
|---|---|---|---|
| NULL | NULL | Valencia | 4 |
| Fuji | 5 | Navel | 5 |
The FULL OUTER JOIN of these two tables, on price, should give the following result:
| variety | price | variety | price |
|---|---|---|---|
| Fuji | 5 | Navel | 5 |
| Gala | 6 | NULL | NULL |
| NULL | NULL | Valencia | 4 |
That’s the result I’ll be working toward in this article. Here is a script to create and populate the example tables, so you can follow along:
create table apples (variety char(10) not null primary key, price int not null);
create table oranges (variety char(10) not null primary key, price int not null);
insert into apples(variety, price) values('Fuji',5),('Gala',6);
insert into oranges(variety, price) values('Valencia',4),('Navel',5);
JOINs and a UNIONOne method to simulate a full join is to take the union of two outer joins, for example,
select * from apples as a
left outer join oranges as o on a.price = o.price
union
select * from apples as a
right outer join oranges as o on a.price = o.price
This gives the desired results in this case, but it isn’t correct
for all cases. Suppose there are duplicate records in the tables
(remove the primary key and insert twice to create this situation). UNION eliminates duplicates, which a full join doesn’t do. UNION ALL isn’t the right answer either, because it will cause spurious duplicates. In fact, UNION
generates two independent result sets and then combines them, so there
is no way to get around this, because the two result sets need to “know
about each other” to produce the right results.
There are legitimate cases where duplicate results are expected and correct. For instance, even when the rows are unique, selecting only certain columns, in which there are duplicates, could cause this situation. This doesn’t apply in relational theory, because a set never has duplicates no matter what, but it does in SQL.
UNION ALL and an exclusion joinOne way to make UNION include only the duplicates I want is to use an exclusion join to eliminate anything from the second result that is already included in the first, like this:
select * from apples as a
left outer join oranges as o on a.price = o.price
union all
select * from apples as a
right outer join oranges as o on a.price = o.price
where a.price is null;
This handles duplicate rows correctly and doesn’t include anything it shouldn’t. It’s necessary to use UNION ALL instead of plain UNION,
which would eliminate the duplicates I want to keep. This may be
significantly more efficient on large result sets, since there’s no
need to sort and remove duplicates.
There’s a case where UNION won’t work: older versions of MySQL don’t support it. All is not lost, though.
I’ve written several articles explaining how to start with a set of mutually exclusive numbers (which I informally call a “mutex table“), then use the mutual exclusivity of the numbers to join things together in interesting ways. This lets me simulate subqueries and unions on earlier versions of MySQL, for example. This approach seems like it might work well here, too. For the following queries I’ll assume my mutex table has the values 0 and 1. Here’s a baseline mutex query for these two tables:
select * from mutex
left outer join apples as a on i = 0
left outer join oranges as o on i = 1;
| i | variety | price | variety | price |
|---|---|---|---|---|
| 0 | Fuji | 5 | NULL | NULL |
| 0 | Gala | 6 | NULL | NULL |
| 1 | NULL | NULL | Valencia | 4 |
| 1 | NULL | NULL | Navel | 5 |
Of course this isn’t a full join. If I want to full join on price, naturally, I need to include price in the join criteria somewhere, and the query above doesn’t even mention the price. But it’s a starting point for tinkering.
The mutex values in the leftmost column, combined with the join
criteria, ensure that every row in the two tables gets included on its
own row. The mutual exclusivity causes the Navel row not to be matched
to the Fuji row, even though they have the same price. The correct
behavior of a full join on price is to “fill in” the NULL values where the prices are equal. This modification to the join criteria will fill it in:
select * from mutex
left outer join apples as a on i = 0
left outer join oranges as o on i = 1 or a.price = o.price;
The or a.price = o.price relaxes the mutual exclusivity, telling the join to keep the rows separated unless they have the same price:
| i | variety | price | variety | price |
|---|---|---|---|---|
| 0 | Fuji | 5 | Navel | 5 |
| 0 | Gala | 6 | NULL | NULL |
| 1 | NULL | NULL | Valencia | 4 |
| 1 | NULL | NULL | Navel | 5 |
That’s getting closer. There is a spurious row, though. The Navel
row at the bottom of the result set shouldn’t be there; it has already
been matched to the Fuji row earlier, so there’s no need to include it
with all those NULLs as though there were no matching row in apples. Can I eliminate the Navel row without eliminating the Valencia row?
That turns out to be harder to do. I stared at it for a while, thinking I could include a WHERE clause that would eliminate spurious rows based on the value of i, but after a bit I got a reality check: the row has already been included above, and WHERE clauses work a row at a time, so there’s no way to assert something about one row while applying the WHERE
clause to another row. This simple fact is all I needed to realize
there’s no way to eliminate the Navel row with the given information.
What I can do, though, is stack another copy of the apples table onto the right-hand side of the results thus far, matching them to the oranges values and confining them to rows with mutex value 1 instead of 0. Now I can write a WHERE clause to see if a row in the i = 1 part of the result set matches a row in the i = 0 part. I’ll write it without the WHERE clause to start:
select * from mutex
left outer join apples as a on i = 0
left outer join oranges as o on i = 1 or a.price = o.price
left outer join apples as a2 on i = 1 and a2.price = o.price;
| i | variety | price | variety | price | variety | price |
|---|---|---|---|---|---|---|
| 0 | Fuji | 5 | Navel | 5 | NULL | NULL |
| 0 | Gala | 6 | NULL | NULL | NULL | NULL |
| 1 | NULL | NULL | Valencia | 4 | NULL | NULL |
| 1 | NULL | NULL | Navel | 5 | Fuji | 5 |
Now there is a way to tell between the Navel row, which I don’t want, and the Valencia, which I do: the Navel has non-NULL values in the rightmost copy of apples, but the Valencia doesn’t. All I have to do is eliminate rows that have matching values:
select * from mutex
left outer join apples as a on i = 0
left outer join oranges as o on i = 1 or a.price = o.price
left outer join apples as a2 on i = 1 and a2.price = o.price
where o.price is null or a2.price is null
If the WHERE clause is hard to understand, perhaps it’s easier to think of it this way: where not(o.price is not null and a2.price is not null). Both clauses are identical; all I did was apply some boolean identities. Here is the result:
| i | variety | price | variety | price | variety | price |
|---|---|---|---|---|---|---|
| 0 | Fuji | 5 | Navel | 5 | NULL | NULL |
| 0 | Gala | 6 | NULL | NULL | NULL | NULL |
| 1 | NULL | NULL | Valencia | 4 | NULL | NULL |
That result has the correct rows, but it has some extra columns, which I don’t need. Here’s the final query:
select a.*, o.* from mutex
left outer join apples as a on i = 0
left outer join oranges as o on i = 1 or a.price = o.price
left outer join apples as a2 on i = 1 and a2.price = o.price
where o.price is null or a2.price is null
Remember, this can’t be done without the mutex table, because I need something to provide non-NULL values for every row; otherwise the joins would not include values where the leftmost table has no rows to contribute.
This technique works if there are duplicate rows, and works on older versions of MySQL, but is probably the least efficient of the three I’ve demonstrated here. As usual, which query is appropriate depends on circumstances.
+--------------------------+----------------------------+ | Variable_name | Value | +--------------------------+----------------------------+ | character_set_client | euckr | | character_set_connection | euckr | | character_set_database | euckr | | character_set_results | euckr | | character_set_server | euckr | | character_set_system | utf8 | | character_sets_dir | /usr/share/mysql/charsets/ | | collation_connection | euckr_korean_ci | | collation_database | euckr_korean_ci | | collation_server | euckr_korean_ci | | concurrent_insert | ON | | connect_timeout | 5 | +--------------------------+----------------------------+
CREATE TABLE t1 ( a INT NOT NULL AUTO_INCREMENT PRIMARY KEY,message CHAR(20)) ENGINE=MyISAM;
CREATE TABLE t2 (
a INT NOT NULL AUTO_INCREMENT PRIMARY KEY,message CHAR(20)) ENGINE=MyISAM;
INSERT INTO t1 (message) VALUES ('Testing'),('table'),('t1');
INSERT INTO t2 (message) VALUES ('Testing'),('table'),('t2');
CREATE TABLE total (a INT NOT NULL AUTO_INCREMENT,
message CHAR(20), INDEX(a))
ENGINE=MERGE UNION=(t1,t2) INSERT_METHOD=LAST;
t1, t2 테이블의 데이터를 total 테이블에 병합된 데이터로 insert 하게 된다.
NSERT_METHOD는 total 테이블에 다이렉트로 INSERT할경우 UNION에
정의한 테이블중 어느 테이블에 INSERT를 할건지 정의하는것이다.(FISRT or LAST)
※ 주의할점은 두 테이블의 레코드 형식 및 타입까지 모두 일치하여야 한다.
1. 설치
모든 프로그램이 그렇듯 다운로드 받아야 합니다. proftpd는 proftpd.org에서 받으실 수 있습니다.
현재 최신버전은 1.2.9입니다. 물론 업그래이드 되더라도 설치법은 크게 변하지 않으니 최신 버전을 받아서 하셔두 됩니다.^^ 만약 설치가 안된다면, 본 강좌의 마지막 부분에 나오는 메일주소로 메일 보내 주시기 바랍니다.~
먼저 설치할 디렉토리로 이동합니다.
통상적으로 프로그램은 /usr/local 아래 디렉토리에 설치하게 되면 .. 관리가 편리합니다.. 모든 프로그램을 그 위치에 설치하고 그 디렉토리만 압축해서 보관하면 서버가 뻣었을 때 복구가 간단하죠^^:
[root@test /]# cd /usr/local/
간단하게 위와 같은 wget 프로그램을 사용하여 다운로드 받습니다.
[root@test local]# wget http://ftp.superuser.co.kr/pub/FTP/proftpd/proftpd-1.2.9.tar.gz
--11:13:51-- http://ftp.superuser.co.kr/pub/FTP/proftpd/proftpd-1.2.9.tar.gz
=> `proftpd-1.2.9.tar.gz'
Resolving ftp.superuser.co.kr... 완료.
Connecting to ftp.superuser.co.kr[210.114.223.221]:80... connected.
HTTP 요청을 보냅니다, 서버로부터의 응답을 기다림...200 OK
길이: 995,350 [application/x-tar]
100%[=================================================================>] 995,350 382.54K/s ETA 00:00
11:13:54 (382.54 KB/s) - `proftpd-1.2.9.tar.gz'가 보존되었습니다 [995350/995350]
압축을 풀어줍니다.~
[root@test local]# tar xvfz proftpd-1.2.9.tar.gz
디렉토리 이동합니다.
[root@test local]# cd proftpd-1.2.9
- 컨피그를 행합니다. 컴파일 환경과 옵션들을 설정할 수 있습니다.
./configure --help
위 명령을 내려 옵션이 뭐가 있는지 확인할 수 있지만, 별 내용없으니 아래의 옵션데로 컨피그를 합니다.
[root@test proftpd-1.2.9]# ./configure --prefix=/usr/local/proftpd --enable-autoshadow \
--enable-shadow
옵션에서 --prefix는 설치할 디렉토리이고, --enable-autoshadow와 --enable-shadow는 대부분의 Linux에 shadow패스워드 시스템을 사용하기 때문에 이렇게 적어 주었습니다.
그리구 는 한줄에 다 못쓸 때 다음줄이랑 이어진다는 의미로 적어줍니다.~ VB의 "_"와 같은 의미죠^^;
- 컴파일 합니다.
[root@test proftpd-1.2.9]# make
- 설치 합니다.
[root@test proftpd-1.2.9]# make install
2. 환경설정파일 편집
이렇게 설치를 하였다면, 환경설정 파일은 /usr/local/proftpd/etc/proftpd.conf 일 것입니다.
- 실행 그릅 변경
27번째 줄을 보면 실행그릅이 nogroup로 되어 있을 것입니다. 지금까지 설치한데로 설치 했었다면, nogroup는 없을 것입니다. nogroup라는 group를 생성해 주던지 아니면, 이 것을 nobody로 해 주면 됩니다.
Group nogroup
==> Group nobody
- wheel 그룹 사용자외에 자기 home 디렉토리 못 벗어나게 설정
다음과같이 한 줄만 추가 하면 됩니다.
DefaultRoot ~ !wheel
- 파일올렸는데 시간이 다르게 나온다면..
파일을 올렸는데 보이는 시간이 다르다면, 다음 옵션을 추가 합니다.
TimesGMT off
- 트래픽 제한 wheel 그룹 사용자만 빼고 모든 사용자에게 .. 300K/sec 로 설정하고 싶다면, 아래 설정은 30M까지는 제한이 없게 설정했습니다. 31457280 이 수치가 30M라는 의미 입니다.
TransferRate APPE,RETR,STOR,STOU 300.0:31457280 group !wheel
기타 다른 기능들을 원하신다면, 아래의 메일 주소로 메일 한통 보내 주세요~ 그럼 제가 생각해보구 제 맘데로 추가 해 두겠습니다.^^;
3. 서버 시작
모든 설정이 끝났다면, 서버(데몬)를 시작합니다.
[root@test etc]# /usr/local/proftpd/sbin/proftpd
이렇게 아무런 메시지가 없다면 시작된 것입니다.
- 확인
[root@test etc]# netstat -an | grep LISTEN | grep 21
tcp 0 0 0.0.0.0:21 0.0.0.0:* LISTEN
위와같이 21번 포트가 정상적으로 열려있어야 합니다.
[root@test etc]# ps -ef | grep proftpd
nobody 7617 1 0 11:53 ? 00:00:00 proftpd: (accepting connections)
root 7627 1682 0 11:54 pts/0 00:00:00 grep proftpd
위와같이 proftpd프로세스트 정상적으로 떠 있어야 합니다.
이렇게 확인하였다면. 서버 부팅시 시작되게 rc.local파일에 넣어줍니다.
[root@test etc]# echo /usr/local/proftpd/sbin/proftpd >> /etc/rc.d/rc.local
4. 기타 프로그램
proftpd는 여러 명령어를 제공합니다.
- ftp 서버 정지
[root@test sbin]# /usr/local/proftpd/sbin/ftpshut
usage: /usr/local/proftpd/sbin/ftpshut [ -R ] [ -l min ] [ -d min ] time [ warning-message ... ]
ftp 서버를 정지하는 명령어입니다. 접속된 사용자에게 메시지를 뿌려 줄 수 있으며, 일정시간후 자동 정지 할 수 있습니다. 개인적으로 즉시 정지 하기 위해서는 ftpshut now 라고 하면 된다는데 ... 테스트 결과 잘 되지 않는군요^;
그럴 때 이렇게 죽이세요^^:
[root@test sbin]# killall proftpd
한방에 죽습니다^^;
그리구 shutmsg가 남아있는 경우가 있습니다. 이 파일이 있으면, ftp 접속이 되지 않으니 있으면 지워 주시기 바랍니다.^^
rm -rf /etc/shutmsg
- ftp서버에 접속한 사용자 수 알아내기
[root@test bin]# /usr/local/proftpd/bin/ftpcount
Master proftpd process 7766:
Service class - 1 user
한사람의 사용자가 접속되어 있다는 것을 알 수있습니다.
- ftp 만의 top 명령이 있다^^;
[root@test bin]# /usr/local/proftpd/bin/ftptop
ftptop/0.9: Tue May 25 12:12:13 2004, up for 5 min
1 Total FTP Sessions: 0 downloading, 0 uploading, 1 idle
PID S USER CLIENT SERVER TIME COMMAND
7794 I doly test 0.0.0.0:21 257 idle
물론 top 명령어와 비슷하게 -d 옵션두 사용할 수 있습니다. 그리고 q를 누르게 되면 빠져 나오죠^^:
- ftp에서두 who 명령어를??
[root@test bin]# /usr/local/proftpd/bin/ftpwho
standalone FTP daemon [7766], up for 7 min
8240 doly [ 0m4s] 0m1s idle
Service class - 1 user
위와같이 접속 정보를 알 수 있습니다.
------------ 설치기
위의 내용데로 설치 해본 후 ftp 접속이 되질 않아 관련 자료를 찾던중..
proftpd.conf 파일에
AuthPAM off (기본값은 on입니다.)
한줄을 추가 함으로써 해결을 했다.
/var/log/message 에서 보니.
`PAM(close_session): System error`
란 에러가 뜨는걸로 봐서 아무래도 PAM 대한 문제 인듯... ㅠ.ㅠ 근데 저넘은 뭐지?