import pandas as pd
from pandas import Series,DataFrame

emp = pd.read_csv("C:\\data\\emp.csv")
emp.info()
emp.HIRE_DATE = pd.to_datetime(emp.HIRE_DATE)   # 문자형 >> 날짜형

import matplotlib.pyplot as plt
from matplotlib import font_manager, rc
font_name = font_manager.FontProperties(fname='C:\\Windows\\Fonts\\malgun.ttf').get_name()
rc('font',family = font_name)

import numpy as np

import sqlite3

import cx_Oracle
from sqlalchemy.engine import create_engine

import re




[문제] 근무일수가 가장 많은 10위까지 직원들의 employee_id, last_name, department_name, 근무일수를 출력해주세요.
    단, sqlite3 이용하세요.
import sqlite3
conn = sqlite3.connect('C:/data/insa.db')   # DB 접속
c = conn.cursor()                           # cursor 생성
c.execute('select * from sqlite_master')    # 테이블 목록 확인
c.fetchall()

c.execute("""
          select e.*, d.department_name
          from(select *
               from (select *, dense_rank() over(order by working_day desc) rank
                     from(select employee_id, last_name, department_id,
                                 (strftime("%s","now") - strftime("%s",hire_date))/86400 working_day
                          from emp))
               where rank <= 10) e, dept d
          where e.department_id = d.department_id
          order by e.rank
          """)
c.fetchall()
c.execute("""
          with emp_working as (
                               select employee_id, last_name, department_id,
                                      (strftime("%s","now")-strftime("%s",hire_date))/86400 working_day
                               from emp),
          
               emp_rank as (select employee_id, last_name, department_id, working_day,
                                   dense_rank() over(order by working_day desc) rank
                            from emp_working),
               
               top_10 as (select * from emp_rank where rank <= 10)
               
          select t.employee_id, t.last_name, d.department_name, t.working_day, t.rank
          from top_10 t, dept d
          where t.department_id = d.department_id
          order by 5
          """)
c.fetchall()

 

[문제] 근무일수가 가장 많은 10위까지 직원들의 employee_id, last_name, department_name, 근무일수를 출력해주세요.
    단, pandas 이용하세요.
import pandas as pd
from pandas import Series,DataFrame

emp = pd.read_csv("C:\\data\\emp.csv")
emp.info()

dept = pd.read_csv("C:\data\dept.csv")
dept.info()

x = DataFrame({'사번':emp.EMPLOYEE_ID,
               '이름':emp.LAST_NAME,
               '부서코드':emp.DEPARTMENT_ID,
               '근무일수':(pd.Timestamp.now()-pd.to_datetime(emp.HIRE_DATE)).dt.days})
x

x['순위'] = x['근무일수'].rank(ascending=False, method='dense').astype('int')
x

top_10 = x[x['순위'] <= 10]
top_10

result = pd.merge(top_10,dept,left_on='부서코드',right_on='DEPARTMENT_ID')
result = result[['사번','이름','DEPARTMENT_NAME','근무일수','순위']].sort_values(by='순위')
result




c.execute("create table insa(id integer, name char, sal integer)")

c.execute("insert into insa(id, name, sal) values(1, 'james', 1000)")
c.execute("select * from insa")
c.fetchall()



# 변수값을 통한 insert

v_id = 2
v_name = '홍길동'
v_sal = 2000

c.execute("insert into insa(id, name, sal) values(?, ?, ?)", (v_id, v_name, v_sal))
c.execute("select * from insa")
c.fetchall()
conn.commit()
c.execute("select * from insa where id = ?", (v_id,)) # 튜플형식에 맞춰 1개 값일 때 끝에 , 넣기
c.fetchall()




# 변수값을 통한 update

v_id = 2
v_name = '박찬호'

c.execute("update insa set name = ? where id = ?", (v_name, v_id))
c.execute("select * from insa")
c.fetchall()




# 변수값을 통한  delete

c.execute("delete from insa where id = ?", (v_id,))
c.execute("select * from insa")
c.fetchall()


conn.rollback()
c.close()
conn.close()

 




■ 파이썬에서 오라클 접속

# 설치

Anaconda Prompt
> conda install -c anaconda cx_oracle
...
Proceed ([y]/n)? y


import cx_Oracle        # 대소문자 주의

# Oracle 리스너 상태를 확인

CMD
> lsnrctl status
...
Default Service           XE
(HOST=DESKTOP-60J2T7A)(PORT=1521)




# 오라클 접속

conn = cx_Oracle.connect('hr', 'hr', 'DESKTOP-60J2T7A:1521/xe', encoding='UTF-8')
                        # 접속계정, 비번, PC명 : 포트 / 서비스명
cursor = conn.cursor()




# 칼럼명 업데이트

cursor.execute('select * from employees')
data = cursor.fetchall()
df = DataFrame(data)
df                      # 컬럼정보가 없다.  

cursor.execute("""
                   SELECT column_name
                   FROM user_tab_columns
                   WHERE table_name = 'EMPLOYEES'
               """)

col = cursor.fetchall()
col                     # list[ tuple() ]

df.columns = [j for i in col for j in i]
df

 

for i in col: print(i[0])
[i[0] for i in col]
[j for i in col for j in i]




# 테이블 생성

cursor.execute("""create table insa(id number, name varchar2(30), day date)""")


< SQL >
이름   널? 유형           
---- -- ------------ 
ID      NUMBER       
NAME    VARCHAR2(30) 
DAY     DATE         




# INSERT

cursor.execute("""insert into insa(id, name, day) values(1, '홍길동', sysdate)""")
cursor.execute('commit')
cursor.execute('select * from insa')
cursor.fetchall()
v_id = 2
v_name = '박찬호'
v_day = pd.Timestamp.now().date()

cursor.execute("""insert into insa(id,name,day) values(:b_id, :b_name, :b_day)""", (v_id, v_name, v_day))
                                                        # 바인드변수로 표시


cursor.execute('commit')
cursor.execute('select * from insa')
cursor.fetchall()
sql = """insert into insa(id, name, day) values(:b_id, :b_name, :b_day)"""
                              
cursor.execute(sql, [3, '손흥민', pd.Timestamp.now()])
cursor.execute('commit')
cursor.execute('select * from insa')
cursor.fetchall()

cursor.execute(sql, b_id=4, b_name='나얼', b_day=pd.Timestamp.now())
cursor.execute('commit')
cursor.execute('select * from insa')
cursor.fetchall()
sql = """insert into insa(id, name, day) values(:b_id, :b_name, :b_day)"""
data = dict(b_id=5, b_name='윤건', b_day=pd.Timestamp.now())

cursor.execute(sql, data)
cursor.execute('commit')
cursor.execute('select * from insa')
cursor.fetchall()




SQL문에 변수 처리하는 이유 ?
실행계획을 공유하기 위한 목적(CPU, 메모리 사용을 줄일 수 있다.)

cursor.execute('select * from insa where id=1')
cursor.fetchall()

cursor.execute('select * from insa where id=2')
cursor.fetchall()
... ↓
cursor.execute('select * from insa where id = :b_id', (1,))
cursor.fetchall()

cursor.execute('select * from insa where id = :b_id', [1])
cursor.fetchall()
... ↓
cursor.execute('select * from insa where id = :b_id', (v_id,))
cursor.fetchall()

cursor.execute('select * from insa where id = :b_id', [v_id])
cursor.fetchall()

cursor.execute('select * from insa where id = :b_id', b_id = v_id)
cursor.fetchall()




# UPDATE

cursor.execute("""update insa set name = '제임스' where id = 2""")
cursor.execute('select * from insa')
cursor.fetchall()

cursor.execute('rollback')
cursor.execute('select * from insa')
cursor.fetchall()
v_id = 2
v_name = '제임스'

cursor.execute("""update insa 
                  set name = :b_name
                  where id = :b_id
               """, b_name = v_name, b_id = v_id)
cursor.execute('select * from insa')
cursor.fetchall()

cursor.execute('rollback')
cursor.execute('select * from insa')
cursor.fetchall()




# DELETE

v_id = 2
cursor.execute("""delete from insa where id = :b_id""", b_id = v_id)
cursor.execute('select * from insa')
cursor.fetchall()

cursor.execute('rollback')
cursor.execute('select * from insa')
cursor.fetchall()

 




<< Pandas DataFrame -> Oracle Table 이관 작업 >>

from sqlalchemy.engine import create_engine

db_sw = 'oracle'
sql_driver = 'cx_oracle'

username = 'hr'
password = 'hr'
host = 'DESKTOP-60J2T7A'
port = 1521
service = 'xe'

path = db_sw+'+'+sql_driver+'://'+username+':'+password+'@'+host+':'+str(port)+'/?service_name='+service
path        # 'oracle+cx_oracle://hr:hr@DESKTOP-60J2T7A:1521/?service_name=xe'

engine = create_engine(path)

emp.to_sql('emp_new', engine, schema='hr', index=False)



< SQL >

desc emp_new 이름             널? 유형         
-------------- -- ---------- 
EMPLOYEE_ID       NUMBER(19) 
FIRST_NAME        CLOB       
LAST_NAME         CLOB       
EMAIL             CLOB       
PHONE_NUMBER      CLOB       
HIRE_DATE         CLOB       
JOB_ID            CLOB       
SALARY            NUMBER(19) 
COMMISSION_PCT    FLOAT(126) 
MANAGER_ID        FLOAT(126) 
DEPARTMENT_ID     FLOAT(126) 
alter table emp_new modify commission_pct number;
alter table emp_new modify manager_id number;
alter table emp_new modify department_id number;
desc emp_new
이름             널? 유형         
-------------- -- ---------- 
EMPLOYEE_ID       NUMBER(19) 
FIRST_NAME        CLOB       
LAST_NAME         CLOB       
EMAIL             CLOB       
PHONE_NUMBER      CLOB       
HIRE_DATE         CLOB       
JOB_ID            CLOB       
SALARY            NUMBER(19) 
COMMISSION_PCT    NUMBER     
MANAGER_ID        NUMBER     
DEPARTMENT_ID     NUMBER   
alter table emp_new modify first_name varchar2(30);

> 오류 : CLOB타입은 문자형(varchar2(4000),char(2000))으로 변환 불가 -> 칼럼추가해서 이관해라.

- CLOB : 문자형. 1개 필드 안에 4GB 저장 가능
1. 새로운 컬럼을 생성
alter table emp_new add new_column varchar2(30);
desc emp_new

3. 새로운 칼럼(varchar2)에 clob의 컬럼의 정보를 대입
update emp_new set new_column = dbms_lob.substr(first_name, 4000, 1);

commit;
select * from emp_new;

3. 기존 clob 컬럼 삭제
alter table emp_new drop column first_name;
desc emp_new

4. 새로운 컬럼 이름을 기존 컬럼 이름으로 수정
alter table emp_new rename column new_column to first_name;
desc emp_new 이름             널? 유형           
-------------- -- ------------ 
EMPLOYEE_ID       NUMBER(19)   
LAST_NAME         CLOB         
EMAIL             CLOB         
PHONE_NUMBER      CLOB         
HIRE_DATE         CLOB         
JOB_ID            CLOB         
SALARY            NUMBER(19)   
COMMISSION_PCT    NUMBER       
MANAGER_ID        NUMBER       
DEPARTMENT_ID     NUMBER       
FIRST_NAME        VARCHAR2(30) 

 




■ 정규표현식(Regular Expression)
특정 패턴과 일치하는 문자열 검색, 치환, 제거하는 기능을 제공한다.


■ 메타문자(meta charachers)
원래 문자가 가진 뜻이 아닌 특별한 용도로 사용하는 문자

[A-Za-z] [ ] 사이의 문자들과 매치
[^] not을 의미
[sql] s 또는 q 또는 l
[가-힣ㄱ-ㅎㅏ-ㅣ] 한글 자모음
\w  또는 [a-zA-Z가-힣0-9]  문자, 숫자 패턴
\W 또는 [^a-zA-Z가-힣0-9] 문자, 숫자가 아닌 패턴
\s  또는 [ \t\n\r\f\v] 공백문자
\S  또는 [^ \t\n\r\f\v] 공백문자가 아닌 패턴
\d  또는 [0-9] 숫자 패턴
\D  또는 [^0-9] 숫자가 아닌 패턴
a.b .(점) 위치에 모든 문자를 의미 (줄바꿈 \n 제외)
a\.b .(점)을 문자로 인식
a[.]b .(점)을 문자로 인식
a|b a 또는 b
^ 시작
$ 종료
a*b * 바로 앞의 문자가 0번 이상을 찾는다. a가 있을 수도 있고 없을 수도 있다. b, ab, aaab
a+b + 바로 앞의 문자가 1번 이상을 찾는다. a가 무조건 있어야 한다. ab, aab
ab?c ? 바로 앞의 문자가 0번, 1번을 찾는다. b가 없거나 1번 있다. ac, abc
a{2}b {n} 바로 앞의 문자가 n번 반복을 찾는다. aab
a{2,3}b {n,m} 바로 앞의 문자가 n번 또는 m번 반복을 찾는다. aab, aaab
a{2,}b {n} 바로ㅇ 앞의 문자가 n번 이상 반복을 찾는다. aab, aaab




import re



re.match : 문자열의 '처음'부터 정규식과 매칭되는지를 찾는 함수.

re.match('\w', '100')
bool(re.match('\w', '100'))
<re.Match object; span=(0, 1), match='1'>
True
re.match('\w', '_')
bool(re.match('\w', '_'))
<re.Match object; span=(0, 1), match='_'>
True
re.match('\w', '-python')
bool(re.match('\w', '-python'))

False
re.match('\W', '-python')
bool(re.match('\W', '-python'))
 <re.Match object; span=(0, 1), match='-'>
True
bool(re.match('[-]', '-python'))
bool(re.match('[-]', 'py-thon'))
True
False
bool(re.match('c*a','ccat')) True
bool(re.match('c+a','ccat'))
bool(re.match('c+a','at'))
True
False
bool(re.match('c?a','cat'))
bool(re.match('c?a','at'))
True
True
bool(re.match('c{2,3}a','cccat')
bool(re.match('c{2,}a','cccccat'))
True
True
bool(re.match('c|a','ct'))
bool(re.match('c|a','at'))
bool(re.match('c|a','bat'))
True
True
False
bool(re.match('[0-9]th','21th'))
bool(re.match('[0-9][0-9]th','21th'))
bool(re.match('[0-9]{2}th','21th'))
bool(re.match('[0-9]*th','21th'))
bool(re.match('[0-9]+th','21th'))
bool(re.match('[0-9]?th','21th'))
bool(re.match('\d\dth','21th'))
bool(re.match('\d{2}th','21th'))
False
True    # 첫번째 자리, 두번째 자리 숫자패턴
True
True
True
False
True
True
re.match('Or','Oracle engineer')
re.match('or','Oracle engineer')
re.match('or','Oracle engineer', re.I)
True    # Or 로 시작?
False
True     # re.I 대소문자 무시
m = re.match('Oracle','Oracle engineer') <re.Match object; span=(0, 6), match='Oracle'>
m.group()
m.span()
m.start()
m.end()

'Oracle enginner'[m.start():m.end()]
'Oracle'    # 매치된 문자열 리턴
(0, 6)    # 매치된 문자열의 시작, 끝-1
0    # 매치된 문자열 시작위치
6    # 매치된 문자열 끝 위치

'Oracle'




re.search : 해당하는 문자열에서 어디서든지 찾는다. 먼저나오는 1개

re.match('engineer', 'Oracle engineer') False
re.search('engineer', 'Oracle engineer') <re.Match object; span=(7, 15), match='engineer'>
m = re.search('engineer','Oracle engineer')
m.group()
m.span()
m.start()
m.end()
<re.Match object; span=(7, 15), match='engineer'>
'engineer'
(7, 15)
7
15




re.findall : 일치하는 문자열 전부를 리스트로 반환한다.

re.search('engineer', 'Oracle engineer Oracle engineer') <re.Match object; span=(7, 15), match='engineer'>
re.findall('engineer', 'Oracle engineer Oracle engineer') ['engineer', 'engineer']



[문제] data 변수에서 숫자 패턴을 찾아주세요.
data = '오늘은 2023년 11월 28일 입니다.'
re.findall('[0-9]+', data)
re.findall('\d+', data)
['2023', '11', '28']

 

[문제] data 변수에서 문자 패턴을 찾아주세요.
data = '오늘은 2023년 11월 28일 입니다.'
re.findall('\D+', data)
re.findall('[^0-9]+', data)     # 숫자가 아닌

re.findall('[가-힣]+', data)
['오늘은 ', '년 ', '월 ', '일 입니다.']
['오늘은 ', '년 ', '월 ', '일 입니다.']

['오늘은', '년', '월', '일', '입니다']




re.sub : 패턴을 치환하는 함수

source = 'oracle engin'
source.replace('engin','engineer') 'oracle engineer'
re.sub('engin', 'engineer', source)

re.sub(기존문자열, 변경문자열, 데이터)




re.split : 패턴을 이용해서 문자열 분리

'python.programming'.split('.') ['python', 'programming']
re.split('\.', 'python.programming')
re.split('[.]', 'python.programming')
re.split('[.:]', 'python.programming:PLSQL') ['python', 'programming', 'PLSQL']
re.split('[.: ]', 'python.programming:PLSQL SQL')
re.split('[.:\s]', 'python.programming:PLSQL SQL')
['python', 'programming', 'PLSQL', 'SQL']