두개의 테이블에서 불일치하는 데이터를 찾는 SQL 명령으로 idx 는 index를 타는 칼럼이다.
select * from favoriteBookwhere staffidx not in (select idx from member);
select * from FavoriteBook where staffidx not in (select idx from SYS_MEMBER);
불일치 데이터를 찾아서 지우는 SQL 문
delete from FavoriteBook where staffidx not in (select idx from member);
delete from FavoriteBook where staffidx not in (select idx from SYS_MEMBER);
select * from FavoriteBook where myidx not in (select idx from SYS_MEMBER); delete from FavoriteBook where myidx not in (select idx from SYS_MEMBER);
누락된 데이터 찾기
SELECT b.fieldB, a.fieldA FROM tableA a RIGHT OUTER JOIN tableB b ON a.fieldA = b.fieldB WHERE a.fieldA IS NULL;
불일치 데이터 찾기
두 테이블의 uid는 서로 같고 특정 필드 업데이트에 따른 불일치가 되는 데이터 찾기
즉 백업 이후에 변경된 데이터 찾기 같은 셈이다.
select a.uid,a.direct,b.direct,a.is_checking,b.is_checking from data_0218 a, data_0220 b where a.uid=b.uid anda.is_direct=5 anda.is_checking=0 and b.is_checking NOT IN(0);
<?php $sql="select * from test where engregexp '가|나|다|라'"; // 정규식을 접목한 or 검색 $s1="가"; $s2="나"; $s3="다"; $s4="라"; $sql = "select * from test where eng regexp'".$s1."|".$s2."|".$s3."|".$s4."' "; echo $sql; ?>
select * from test where eng regexp 'do?'; // ?는 마지막 글자가 0 또는 1개 일치 : d 또는 do가 일치 select * from test where name regexp '^..$'; // 시작부터 2글자로 끝나는 이름을 찾아라.
select * from test where eng regexp '^I'; // 첫글자가 I로 시작되는 문장을 찾아라
select * from test where eng regexp '^I\''; // 첫글자가 I'로 시작되는 문장을 찾아라
SELECT * FROM test WHERE col1 REGEXP '^[0-9]+$'; // 숫자로만 된 것을 찾아라
SELECT * FROM test WHERE col1 REGEXP '^[[:digit:]]+$'; // 숫자로만 된 것을 찾아라
SELECT * FROM test WHERE col1 NOT REGEXP '^[0-9]+$'; // 숫자로만 된 것을 제외하고 찾아라
SELECT * FROM test WHERE length(col1) > 0 and col1 NOT REGEXP '^[0-9]+$'; // 숫자 제외하고 찾아라
SELECT * FROM test WHERE col1 REGEXP '[0-9]+'; // 숫자와 문자를 포함하는 것을 찾아라
PL/SQL(Procedure Language / Structured Query Language)은 오라클 DBMS에서 SQL 언어를 확장하기 위해 사용하는 컴퓨터 프로그래밍 언어. 주로 자료 내부에서 SQL 명령문만으로 처리하기에는 복잡한 자료의 저장이나 프로시저와 트리거 등을 작성하는 데 쓰인다.
주석 처리는 -- 또는 /* */
CREATE OR REPLACE PROCEDURE 프로시져이름 ( 변수명 IN DATATYPE -- Mode를 생략하면 default 로 IN 변수명 OUT DATATYPE 변수명 IN OUT DATATYPE ) IS [선언문]; -- 변수의 선언
-- v_cnt NUMBER := 0; --프로시저 선언부에서 변수초기화하는 방법 BEGIN 실행문; [EXCEPTION] END; /
/* 예제 */
CREATE OR REPLACE PROCEDURE update_sal ( /* IN Parameter */ v_empno IN NUMBER ) IS BEGIN
UPDATE emp SET sal = sal * 1.1 WHERE empno = v_empno; -- PL/SQL 블록 내에서는 한 문장이 종료할 때마다 세미콜론(;) 을 사용.
COMMIT; -- 프로시저는 자동 커밋되지 않으므로 반드시 INSERT, UPDATE, DELETE 에서 COMMIT 해야한다. END update_sal; / -- PL/SQL 블록은 행에 / 가 있으면 종결됨.
교육은 받았는데 오라클 DB를 만질 일이 없다보니 교육도 집중해서 듣지 않아 세부적인 것은 잘 모른다.
그냥 간단한 것만 기록차원에서 적어둔다.
Function은 반드시 그 결과값을 리턴한다.
CREATE OR REPLACE FUNCTION 함수명 ( 변수명 [MODE] DATATYPE, 변수명 [MODE] DATATYPE ) RETURN 리턴할데이터타입 --값 IS [선언문]; BEGIN 실행문; [EXCEPTION] END; /
CREATE OR REPLACE FUNCTION get_table_size (p_table_name varchar2)
return number
is outval number; BEGIN select ROUND(SUM(bytes)/1024/1024) INTO outval FROM user_segments WHERE segment_name = p_table_name and segment_type ='TABLE' group by segment_name; RETURN outval; END; /
MS-SQL 에 대한 사항이지만, primary key 정의는 SQL 이 거의 동일하다고 보면 된다.
primary key
테이블에 대해 primary key 제약 조건을 지정하면 데이터베이스 엔진은 primary key 열에 대해 고유 인덱스를 자동으로 만들어 데이터 고유성을 적용한다. 또한 쿼리에서 primary key가 사용되는 경우 이 인덱스를 사용하여 데이터에 빠르게 액세스할 수 있다. primary key 제약 조건이 두 개 이상의 열에 정의되는 경우 한 열에 중복된 값이 있을 수 있지만 primary key 제약 조건 정의에 있는 모든 열의 값 조합은 각각 고유해야 한다.
라고 나온다.
FOREIGN KEY
primary key 제약 조건과 달리 외래 키 제약 조건을 만들어도 해당 인덱스가 자동으로 생성되지 않는다. 외래 키 열은 쿼리에서 한 테이블의 외래 키 제약 조건 열을 다른 테이블의 기본 또는 고유 키 열과 연결하여 테이블의 데이터를 병합하는 조인에서 자주 사용된다. 데이터베이스 엔진 에서는 인덱스를 만들어 외래 키 테이블에 있는 관련 데이터를 빠르게 찾을 수 있다.
foreign key (칼럼명) references 부모테이블명(부모칼럼명) on delete cascade;
restrict
참조하는 부모테이블의 칼럼(column)이 삭제되어도 지우지 마라.
cascade
참조하는 부모테이블의 칼럼(column)이 삭제되면 자식 테이블의 칼럼도 모두 삭제하라
set null
참조하는 부모테이블의 칼럼(column)이 삭제되면 자식 테이블의 칼럼이 모두 null이 된다.
no action
참조하는 부모테이블의 칼럼(column)이 삭제되어도 무시하라.
set default
참조하는 부모테이블의 칼럼(column)이 삭제되면 지정된 값으로 대체하라.
on delete rule 또는 on update rule 로 옵션을 지정할 수 있는데, 옵션을 주지 않으면 삭제와 변경이 제한된다.
primary key를 제대로 정의하는 것은 데이터베이스 디자인에 있어서 매우 중요한 출발점이다. 모든 테이블에는 primary key가 있어야 하며, 오직 하나의 primary key만 존재할 수 있다. 회원 테이블을 설계할 때 primary key 로 정의하는 칼럼은 uid 또는 idx 를 많이 사용한다. id 또는 userID 칼럼은 중복없이 생성되도록 하기 위해서 unique index 를 설정한다. primary key의 유무가 엄청난 성능 차이를 보인다. 일반적으로 primary key를 기준으로 데이터를 select 한다거나 primary key를 기준으로 다른 컬럼(들)의 값을 update 또는 delete하는 작업이 흔히 수행되기 때문에 테이블에 primary key를 정의해 주면 where 조건절에 primary key가 검색조건으로 사용된 쿼리들의 성능은 현저하게 향상된다. primary key를 생성하지 않아서 primary key를 사용하는 쿼리들의 성능이 나쁜 것은 물론이며, 테이블에 잘못된 중복 데이터들이 저장됨으로 인하여 데이터 무결성까지 손상되어 있는 비극적인 상황까지 발전한 경우도 있다.
Primary key 컬럼은 반드시 NOT NULL로 정의해야 한다.
KEY `site` (`site`) : 인덱스 설정이 되어 있다는 의미다.
MySQL 의 인덱스는 여러개를 설정해도 실제 동작될 때에는 1개만 동작된다.
Oracle 은 여러개의 인덱스를 자동으로 선택하여 최적의 알고리즘으로 동작한다고 들었다.
따라서, 어떤 인덱스가 동작되도록 할 것인지는 설계자의 몫이다.
테이블 설계시 하나의 테이블에 모든 데이터가 저장되도록 하지 않고 다른 테이블에 분산해서 저장되도록 설계를 잘 하는 것이 중요하다.
테이블 검색시 문자열을 검색하는 것보다 숫자를 검색하면 성능이 훨씬 우수하다.
검색할 결과가 문자열이라고 해도 테이블 설계시 숫자로 검색되도록 설계하여 성능 향상이 되도록 하는 것도 필요하다.
CREATE TABLE IF NOT EXISTS `rb_s_mbrid` ( `uid` int(11) NOT NULL AUTO_INCREMENT, `site` int(11) NOT NULL DEFAULT '0', `id` varchar(50) NOT NULL DEFAULT '', `pw` varchar(50) NOT NULL DEFAULT '', PRIMARY KEY (`uid`), KEY `site` (`site`), KEY `id` (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
CREATE TABLE IF NOT EXISTS `rb_s_mbrdata` ( `memberuid` int(11) NOT NULL, `site` int(11) NOT NULL DEFAULT '0', `auth` tinyint(4) NOT NULL DEFAULT '0', `sosok` int(11) NOT NULL DEFAULT '0', `level` int(11) NOT NULL DEFAULT '0', `comp` tinyint(4) NOT NULL DEFAULT '0', `admin` tinyint(4) NOT NULL DEFAULT '0', `adm_view` text NOT NULL, `email` varchar(50) NOT NULL DEFAULT '', `name` varchar(30) NOT NULL DEFAULT '', `nic` varchar(50) NOT NULL DEFAULT '', `grade` varchar(20) NOT NULL DEFAULT '', `photo` varchar(200) NOT NULL DEFAULT '', `home` varchar(100) NOT NULL DEFAULT '', `sex` tinyint(4) NOT NULL DEFAULT '0', `birth1` smallint(6) NOT NULL DEFAULT '0', `birth2` smallint(4) unsigned zerofill NOT NULL DEFAULT '0000', `birthtype` tinyint(4) NOT NULL DEFAULT '0', `tel1` varchar(14) NOT NULL DEFAULT '', `tel2` varchar(14) NOT NULL DEFAULT '', `zip` varchar(6) NOT NULL DEFAULT '', `addr0` varchar(6) NOT NULL DEFAULT '', `addr1` varchar(200) NOT NULL DEFAULT '', `addr2` varchar(100) NOT NULL DEFAULT '', `job` varchar(30) NOT NULL DEFAULT '', `marr1` smallint(6) NOT NULL DEFAULT '0', `marr2` smallint(4) unsigned zerofill NOT NULL DEFAULT '0000', `sms` tinyint(4) NOT NULL DEFAULT '0', `mailing` tinyint(4) NOT NULL DEFAULT '0', `smail` tinyint(4) NOT NULL DEFAULT '0', `point` int(11) NOT NULL DEFAULT '0', `usepoint` int(11) NOT NULL DEFAULT '0', `money` int(11) NOT NULL DEFAULT '0', `cash` int(11) NOT NULL DEFAULT '0', `num_login` int(11) NOT NULL DEFAULT '0', `pw_q` varchar(250) NOT NULL DEFAULT '', `pw_a` varchar(100) NOT NULL DEFAULT '', `now_log` tinyint(4) NOT NULL DEFAULT '0', `last_log` varchar(14) NOT NULL DEFAULT '', `last_pw` varchar(8) NOT NULL DEFAULT '', `is_paper` tinyint(4) NOT NULL DEFAULT '0', `d_regis` varchar(14) NOT NULL DEFAULT '', `tmpcode` varchar(50) NOT NULL DEFAULT '', `sns` text NOT NULL, `addfield` text NOT NULL, `cp` int(11) DEFAULT '0', `mcp` int(11) DEFAULT '0', `cpcode` int(11) DEFAULT NULL, `recommand` varchar(60) DEFAULT NULL, `broadcast` int(5) NOT NULL DEFAULT '0', PRIMARY KEY (`memberuid`), KEY `site` (`site`), KEY `auth` (`auth`), KEY `comp` (`comp`), KEY `sosok` (`sosok`), KEY `level` (`level`), KEY `admin` (`admin`), KEY `email` (`email`), KEY `name` (`name`), KEY `nic` (`nic`), KEY `sex` (`sex`), KEY `birth1` (`birth1`), KEY `birth2` (`birth2`), KEY `birthtype` (`birthtype`), KEY `addr0` (`addr0`), KEY `job` (`job`), KEY `marr1` (`marr1`), KEY `marr2` (`marr2`), KEY `sms` (`sms`), KEY `mailing` (`mailing`), KEY `smail` (`smail`), KEY `point` (`point`), KEY `usepoint` (`usepoint`), KEY `now_log` (`now_log`), KEY `d_regis` (`d_regis`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
테이블 칼럼을 조인(JOIN)을 고려하여 명칭이 가급적이면 중복되지 않게 처리하는 것이 편하고 중요하다는 걸 느끼고 있다.
// 테이블 칼럼 변경/추가
ALTER TABLE MEMBER ADD mfoneNO VARCHAR( 16 ) NULL DEFAULT NULL; ALTER TABLE MEMBER ADD phoneBrand VARCHAR( 20 ) NULL DEFAULT NULL; ALTER TABLE MEMBER ADD phoneModel VARCHAR( 20 ) NULL DEFAULT NULL;
ALTER TABLE MEMBER ADD `isUpdate` TINYINT( 2 ) NOT NULL DEFAULT '0' AFTER `isGanbu`; ALTER TABLE MEMBER ADD `isDelete` TINYINT( 2 ) NOT NULL DEFAULT '0' AFTER `isUpdate`;
ALTER TABLE AccessLog CHANGE idx uid INT( 11 ) NOT NULL AUTO_INCREMENT; ALTER TABLE AccessLog CHANGE userID LogID VARCHAR( 60 ) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL; ALTER TABLE AccessLog CHANGE userNM LogNM VARCHAR( 20 ) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL ; ALTER TABLE AccessLog ADD date CHAR( 8 ) NOT NULL AFTER ipaddr,ADD YM CHAR( 6 ) NOT NULL AFTER date,ADD MD CHAR( 4 ) NOT NULL AFTER YM ; update AccessLog a, AccessLog b SET a.date=concat(a.year,a.month,a.day),a.YM=concat(a.year,a.month),a.MD=concat(a.month,a.day) where a.uid=b.uid;
// 테이블 칼럼 삭제 ALTER TABLE AccessLog DROP date,DROP YM,DROP MD;
// 임시테이블에 저장하기
가장 최근에 접속한 날짜 기준으로 정렬하고 LogID 기준으로 1개씩만 추출하여 저장
INSERT INTO AccessLog_tmp (ipaddr,date,YM,MD,year,month,day,time,OS,browser,LogID,LogNM,hit) (SELECT ipaddr,max(date),YM,MD,year,month,day,time,OS,browser,LogID,LogNM,hit FROM AccessLog group by LogID order by date DESC );
PHP 에서 코드를 작성시
$sql ="INSERT INTO AccessLog_tmp (ipaddr,date,YM,MD,year,month,day,time,OS,browser,LogID,LogNM,hit) (SELECT ipaddr,max(date),YM,MD,year,month,day,time,OS,browser,LogID,LogNM,hit FROM AccessLog group by LogID order by date DESC )"; @mysql_query($sql);
SELECT id, sum(count), state FROM test group by id; SELECT id, sum(count), state FROM test where state='a' group by id; SELECT id, sum(count), state FROM test where state='b' group by id;
그런 다음에 실제 구하려는 쿼리문을 작성한다.
SELECT id, sum(case when state='a' then count else 0 end) as a_count, sum(case when state='b' then count else 0 end) as b_count FROM test group by id;
또는
SELECT id, sum(if(state='a', count, 0)) as a_count, sum(if(state='b', count, 0)) as b_count FROM test group by id;
자료를 검색해서 찾는 방법
구글에서 검색어 sql sum if condition 를 입력하고 자료를 찾으면 내가 원하는 걸 찾아볼 수 있다.
키포인트는 sum 인데 조건 sum 인거다. 그러므로 조건이 들어가는 sum 과 if 를 검색해보면 된다.
지식인에 오라클 SQL 인데 MYSQL 에서 에러가 발생한다는 질문이 있어서 테이블을 생성해봤다.
CREATE TABLE IF NOT EXISTS `department` ( `DEPTNO` int(11) unsigned NOT NULL, `DEPTNAME` varchar(10) DEFAULT NULL, `FLOOR` int(11) DEFAULT NULL, PRIMARY KEY (DEPTNO) ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
INSERT INTO DEPARTMENT VALUES(1, '영업', 8); INSERT INTO DEPARTMENT VALUES(2, '기획', 10); INSERT INTO DEPARTMENT VALUES(3, '개발', 9); INSERT INTO DEPARTMENT VALUES(4, '총무', 7);
-- EMPLOYEE 테이블 생성 CREATE TABLE IF NOT EXISTS `employee` ( `EMPNO` int(11) unsigned NOT NULL, `EMPNAME` varchar(10) DEFAULT NULL, `TITLE` varchar(10) DEFAULT '사원', `MANAGER` int(11) DEFAULT NULL, `SALARY` int(11) NOT NULL, `DNO` int(5) NOT NULL DEFAULT '1', PRIMARY KEY (EMPNO), CHECK(SALARY < 6000000), CHECK (DNO IN (1,2,3,4)), FOREIGN KEY(MANAGER) REFERENCES employee(EMPNO) ON DELETE SET NULL ON UPDATE CASCADE, FOREIGN KEY(DNO) REFERENCES department(DEPTNO) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
INSERT INTO EMPLOYEE VALUES(4377, '이성래', '사장', NULL, 5000000, 2); INSERT INTO EMPLOYEE VALUES(3426, '박영권', '과장', 4377, 3000000, 1); INSERT INTO EMPLOYEE VALUES(3011, '이수민', '부장', 4377, 4000000, 3); INSERT INTO EMPLOYEE VALUES(3427, '최종철', '사원', 3011, 1500000, 3); INSERT INTO EMPLOYEE VALUES(1003, '조민희', '과장', 4377, 3000000, 2); INSERT INTO EMPLOYEE VALUES(2106, '김창섭', '대리', 1003, 2500000, 2); INSERT INTO EMPLOYEE VALUES(1365, '김상원', '사원', 3426, 1500000, 1);
오라클 EMPLOYEE 테이블 생성 CREATE TABLE IF NOT EXISTS `employee` ( `EMPNO` int(11) unsigned NOT NULL, `EMPNAME` varchar(10) DEFAULT NULL, `TITLE` varchar(10) DEFAULT '사원', `MANAGER` int(11) DEFAULT NULL, `SALARY` int(11) NOT NULL, `DNO` int(5) NOT NULL DEFAULT '1', PRIMARY KEY (EMPNO), check(SALARY < 6000000), CHECK (DNO IN (1,2,3,4)), FOREIGN KEY(MANAGER) REFERENCES employee(EMPNO)ON DELETE SET NULL ON UPDATE CASCADE, FOREIGN KEY(DNO) REFERENCES department(DEPTNO) ON DELETE CASCADE ON UPDATE CASCADE ) ;
NULL 은 INDEX 만들어지지 않는다. NULL 이 많은 항목은 INDEX 부적합하다.
INDEX란, 데이터의 고유성을 확인하거나 검색 성능을 향상시키기 위해 사용하는 객체이다.
특정 칼럼에 INDEX를 생성하면, 해당 칼럼의 데이터들을 정렬하여 별도의 공간에 데이터의 물리적 주소와 함께 저장한다.
INDEX의 가장 중요한 기능은 데이터의 조회 속도를 빠르게 하는 것이다.
속도 향상을 위해 index를 설정한다. INDEX 를 걸었지만, SELECT 문이 제대로 인덱스를 타고 있는지 알아볼 필요가 있다. MySQL 에서 쿼리의 실행 계획을 확인하려면 explain 명령을 사용하면 된다.
explain 명령은 SELECT 문 앞에 explain SELECT 를 붙이기만 하면 된다.
const : 매치되는 행이 하나만 존재
하나의 행이기 때문에 각 컬럼값은 나머지 연산에서 상수로 간주되며, 처음 한번만 읽어들이면 되기 때문에 엄청 빠르다.
possible_keys : 쿼리를 튜닝하는데 도움이 안되므로 possible_keys 칼럼은 그냥 무시해도 된다.
key : 사용된 인덱스를 나타내고, 쿼리를 튜닝할 때 key 칼럼에 의도했던 인덱스가 표시되는지
확인하는 것이 중요하다.
실행계획의 type 이 ALL 이면 key 칼럼은 NULL로 표시된다.
실행계획의 type 이 index_merge 가 아닌 경우에는 반드시 테이블당 하나의 인덱스만 이용할 수 있다.
key_len : 사용된 가장 긴 key 부분을 나타. 실제 업무는 다중 칼럼으로 만들어진 인덱스가 더 많다.
쿼리를 처리하기 위해 다중 칼럼으로 구성된 인덱스에서 몇개의 칼럼이 사용되었는지 알려준다.
ref : 접근 방법이 ref 방식이면 참조 조건으로 어떤 값이 제공되었는지 보여준다.
range : 인덱스를 하나의 값이 아니라 범위로 검색하는 경우를 의미하는데, 주로 <, >, <=, >=, IS NULL, BETWEEN, IN, LIKE 등의 연산자를 이용해 인덱스를 검색할 때 사용된다.
모든 쿼리가 이 접근 방법만 사용해도 어느 정도의 성능을 보장된다고 볼 수 있다.
- ref 컬럼은 이 타입의 조인에서 NULL 임
LIKE 검색을 할 경우 FULL SCAN 하는지 확인이 가능하다.
ALL : Full TABLE Scan 된다.
대부분의 경우에 아주 느린 성능을 보인다.가장 마지막에 선택되는 가장 비효율적인 방법이다. 빠른 응답을 사용자에게 보내줘야 하는 웹서비스와 같은 환경에서는 적합하지 않다. LIKE 검색시 앞에 %를 붙이지 않으면 INDEX 를 탄다는 걸 알 수 있다.
이제 인덱스를 제거하고 다시 explain 해보자.
mysql > ALTERTABLE`bbs_file`DROPINDEX`mp3file`;
WHERE 조건에 사용되는 칼럼 mp3file 을 INDEX를 삭제하고 나서 LIKE 검색을 했더니 ALL(Full Scan)으로 변경되었다.
WHERE 조건에 사용되는 칼럼에 INDEX 가 설정되어 있어야만 검색속도가 빠르게 될 수 있다는 걸 확인했다.
mysql > ALTER TABLE `bbs_file` ADD UNIQUE (`mp3file`); -- mp3file 칼럼에 인덱스 추가
이제 두개의 테이블을 조인해서 원하는 결과를 조회하는 걸 해보자.
explain select d.uid, d.no, f.rand from bbs_data d, bbs_file f where d.no = f.no; explain select d.uid, d.no, f.rand from bbs_file f, bbs_data d where f.no = d.no;
조인하는 테이블 순서는 상관이 없다는 걸 테스트해서 확인했다.
처음에는 ALL(Full Scan) 하고 다음에는 eq_ref 가 나왔다.
eq_ref : 조인수행을 위해 각 테이블에서 하나씩의 행만이 읽혀지는 형태.
조인(INNER JOIN) 에서 두번째 이후에 읽는 테이블(bbs_file)에서 반드시 1건만 존재한다는 보장이 있어야 사용할 수 있는 접근 방법이다.
- const 타입이외에 가장 훌륭한 조인타입이다.
- 조인연산에 PRIMARY KEY 나 UNIQUE index 인덱스가 사용되는 경우.
- 인덱스된 컬럼이 = 연산에 사용되는 경우.
- 비교되는 값은 상수이거나 이전조인결과의 컬럼값일 수 있다.
explain SELECT uid, no, (select classname from bbs_category where uid=bbs_data.category1) as cate1, (select classname from bbs_category where uid=bbs_data.category2) as cate2 from bbs_data;