Hướng dẫn học SQL cho người mới bắt đầu với Oracle

1- Giới thiệu

Tài liệu này hướng dẫn cho những người mới bắt đầu làm quen với SQL. Tài liệu hướng dẫn dựa trên:
  • ORACLE 11g

Nội dung bao gồm
  • Ngôn ngữ SQL
  • Ngữ pháp SQL (Ngữ pháp cơ bản, tiêu chuẩn cho mọi loại DB)
  • Ngữ pháp SQL riêng của Oracle.

2- Bắt đầu nhanh

Ví dụ truy vấn (Query)

Với Oracle, tốt nhất bạn sử dụng một công cụ trực quan, ở đây tôi sử dụng PL/SQL Developer.

3- Một số "thứ" đặc biệt của Oracle

Trước hết chúng ta cần tìm hiểu một chút về một số "thứ" đặc biệt của Oracle. Nó có thể không phải là tiêu chuẩn của các DB khác.

3.1 - Dual

Dual giống như một bảng ảo, nó có tại mọi SCHEMA. Và vì vậy bạn có thể truy vấn nó giống một bảng thông thường.
[Select * from Dual;
Select 'Dual is special table in Oracle' Column_Title From Dual;]

3.2- Sequence

Sequence là một đối tượng để tạo ra các số tăng dần.
Ví dụ tạo ra một Sequence có tên My_Sequence.
[-- Tạo mới một Sequence có tên My_Sequence;
Create Sequence My_Sequence;
]

Lúc đó trên công cụ trực quan bạn có thể nhìn thấy Sequence bạn vừa tạo ra.
[-- Mỗi lần gọi Nextval giá trị của My_Sequence sẽ tăng dần.
-- Hãy thử thực thi lệnh này vài lần.
Select My_Sequence.Nextval From Dual;
-- Bạn có thể sử dụng Currval để lấy ra giá trị hiện thời của My_Sequence.
Select My_Sequence.Currval From Dual;]

4- Các lệnh PL/SQL cơ bản

Ở đây tôi giới thiệu tổng quan về các lệnh cơ bản của PL/SQL. Bạn sẽ hiểu hơn về nó thông qua các ví dụ ở các phần tiếp theo.

4.1- Lệnh If-elsif-else

Cú pháp:

[
IF <điều kiện 1> THEN
    Công việc 1;
[ELSIF <điều kiện 2> THEN
     Công việc 2;
]
[ELSE
     Công việc n + 1;
]
END IF;
]

Ví dụ:
[
If v_Option = 1 Then
   v_Action := 'Run';
Elsif v_Option = 2 Then
   v_Action := 'Backup';
Elsif v_Option = 3 Then
   v_Action := 'Stop';
Else
   v_Action := 'Invalid';
End If;
]

4.2- Vòng lặp không định trước (LOOP)

Cú pháp:
[
LOOP
 -- Làm gì đó tại đây.
EXIT WHEN <Điều kiện thoát khỏi vòng lặp>;
END LOOP;
]

Ví dụ:
[
x := 0;
Loop
 x := x + 1;
 y := y - x;
Exit When x > y;
End Loop;
]

4.3- Vòng lặp có định trước (FOR LOOP)

Cú pháp:
[
FOR v_Index IN <Cận dưới> .. <Cận trên>
LOOP
 -- Làm gì đó tại đây.
END LOOP;
]

Ví dụ:
[
x := 0;
For v_Idx In 1 .. 100 Loop
 x := x + 1;
End Loop;
]

4.4- Vòng lặp while (WHILE)

Cú pháp:
[
WHILE <Điều kiện> LOOP
 -- Làm gì đó tại đây
END LOOP;
]

Ví dụ:
[
v_Text Varchar2(100);
...

While Length(v_Text) < 50 Loop
   v_Text := v_Text || '00';
End Loop
]

5- Bắt đầu với PL/SQL sử dụng PL/SQL Developer

Trước hết bạn cần mở PL/SQL Developer và đăng nhập vào


Trên PL/SQL Developer tạo mới một cửa sổ SQL:

Viết một đoạn code đơn giản tính tổng 2 số.
[
Declare
  v_Result Number;
  -- Khai báo một biến có giá trị 50
  v_a Number := 50;
  -- Khai báo một biến có giá trị 100
  v_b Number := 100;
Begin
  -- In ra màn hình Console
  Dbms_Output.Put_Line('v_a= ' || v_a);
  -- In ra màn hình Console
  Dbms_Output.Put_Line('v_b= ' || v_b);
  -- Tính tổng
  v_Result := v_a + v_b;
  -- In ra màn hình Console
  Dbms_Output.Put_Line('v_Result= ' || v_Result);
End;
]
Nhấn biểu tượng Tam giác màu xanh (như ảnh) hoặc nhấn F8 để thực thi đoạn code.

Mở sang Tab Ouput xem kết quả chạy ví dụ:

6- Các kiểu dữ liệu thông dụng và khai báo

6.1- Kiểu dữ liệu số

NUMBER(prec, scale) Kiểu dấu chấm cố định (Fixed-point) với giá trị tuyệt đối từ 1E-130 tới (không bao gồm) 1.0E126. Một biến NUMBER cũng có thể mô tả 0.
DECIMAL(prec, scale) Kiểu dấu chấm cố định, tiêu chuẩn IBM với độ chính xác tối đa 38 số thập phân.
NUMERIC(pre, secale) Loại số thực (Floating type) với độ chính xác tối đa 38 số thập phân.
FLOAT Loại số chấm động, số thực tiêu chuẩn ANSI và IBM với độ chính xác tối đa 126 số nhị phân (khoảng 38 số thập phân).
INT Kiểu số nguyên, tiêu chuẩn ANSI với độ chính xác tối đa 38 chữ số thập phân
INTEGER Kiểu số nguyên, tiêu chuẩn ANSI và IBM với độ chính xác 38 chữ số thập phân

Các kiểu số thông dụng nhất:
Kiểu số Khai báo sử dụng
Number v_Amount Number(15,2)   hoặc  v_Salary Number;
Integer v_Age Integer;
Float v_Amount Float;
Double  
Real 

6.2- Kiểu text

Thông dụng nhất:
Kiểu text Khai báo sử dụng
Varchar2 v_First_Name Varchar2(32);
NVarchar2 v_First_Name NVarchar2(32);

6.3- Kiểu Date/time

Kiểu dữ liệu Khai báo sử dụng
Date v_Birthday Date;








7 - Các câu lệnh truy vấn (Query)

7.1- SQL Select


Câu lệnh Select là một câu lệnh cơ bản nhất trong SQL, mục đích là truy vấn dữ liệu trong các bảng (Table).
  • BTDQUAN: Bảng dữ liệu Danh mục Quận huyện.
[
-- Truy vấn dữ liệu trên bảng BTDQUAN
-- Truy vấn toàn bộ các cột.
Select * From mqsofthaugiang.btdquan;
-- Câu lệnh truy vấn trên và câu lệnh dưới đây là tương đương nhau.
-- Pty là alias (tên bí danh đặt cho bảng BTDQUAN).
Select qu.* From mqsofthaugiang.btdquan qu;
-- Truy vấn một vài cột
-- Có thể liệt kê ra.
Select qu.maqu, qu.tenqu
From   mqsofthaugiang.btdquan qu;
]


7.2- SQL Distinct


Câu lệnh Distinct được sử dụng cùng với Select, dùng để select các dữ liệu, bỏ qua các dữ liệu trùng nhau. Cú pháp là:
[
Select distinct <column1>, <column2>  ....
]
Xem ví dụ:
[
-- Truy vấn Quận huyện
-- Cột mã quận, tên và mã tỉnh thành.
Select qu.maqu, qu.tenquan, qu.matt
From   mqsofthaugiang.btdquan qu;
-- Truy vấn các matt  trong bảng btdquan.
-- Dữ liệu là nhiều, nhưng trùng nhau.
Select qu.matt from mqsofthaugiang.btdquan qu;
-- Cần sử dụng Distinct để loại bỏ việc trùng lặp.
Select Distinct qu.matt from mqsofthaugiang.btdquan qu;
]
Kết quả chạy các câu lệnh trên:

7.3- SQL Where

Where là câu lệnh nhằm giới hạn phạm vi tìm kiếm. Chẳng hạn bạn muốn tìm kiếm những quận có mã tỉnh thành là 501.
matt = 501.
Bạn cần truy vấn trong bảng BTDQU, tại các vị trí có matt= 501
[
-- Truy vấn bảng BTDQU tìm các Quận có kiểu:
-- matt = 501
Select * From mqsofthaugiang.btdquan qu Where qu.matt = 501;
]
Kết quả truy vấn:

7.4- SQL And Or (Và, hoặc)

And và Or là 2 lệnh điều kiện sử dụng trong where:
Chẳng hạn bạn muốn tìm danh sách các quận huyện có tên bắt đầu bởi chữ Q và thuộc Tỉnh có mã là 501.
[
-- Câu lệnh tìm kiếm các Quận huyện có tên bắt đầu bởi chữ Q.
Select qu.maqu, qu.tenquan, qu.matt
From   mqsofthaugiang.btdquan qu
Where  qu.tenquan Like 'Q%';
-- Câu lệnh tìm kiếm các Quận huyện có tên bắt đầu bởi chữ Q
-- và thuộc Tỉnh có mã là 501.
Select qu.maqu, qu.tenquan, qu.matt
From   mqsofthaugiang.btdquan qu
Where  qu.tenquan Like 'Q%' And qu.matt=501;
]
Kết quả chạy các câu truy vấn:
Ví dụ với câu lệnh Or.
[
-- Tìm kiếm các Quận huyện có tên bắt đầu bởi chữ Q hoặc H.
-- và thuộc Tỉnh có mã là 501
Select qu.maqu, qu.tenquan, qu.matt
From   mqsofthaugiang.btdquan qu
Where  (qu.tenquan Like 'Q%' Or qu.tenquan Like 'H%') And qu.matt=501;
]
Kết quả chạy câu lệnh:

7.5- SQL IN (Trong phạm vi..)

Toán tử IN cho phép kiểm tra một giá trị có thuộc vào một tập hợp cố định các giá trị nào đó không. Và nó có thể làm việc với tất cả các loại kiểu dữ liệu.
[
-- Câu lệnh này tìm kiếm các Quận huyện có tên
-- Quận Cẩm Lệ hoặc Quận Liên Chiểu hoặc Quận Thanh Khê.
Select qu.maqu, qu.tenquan, qu.matt
From   mqsofthaugiang.btdquan qu
Where  qu.tenquan In (N'Quận Cẩm Lệ', N'Quận Liên Chiểu', N'Quận Thanh Khê');
]
Lấy điều kiện đối với Tiếng Việt phải thêm chữ N trước đoạn text.
Kết quả chạy câu lệnh:

7.6- SQL Between (Nằm giữa ...)

[
-- Tìm các Quận huyện có mã tỉnh thành trong khoảng từ 500 đến 503.
Select qu.maqu, qu.tenquan, qu.matt
From   mqsofthaugiang.btdquan qu
Where  (qu.matt Between 500 And 503);
-- Câu lệnh trên tương đương với:
Select qu.maqu, qu.tenquan, qu.matt
From   mqsofthaugiang.btdquan qu
Where  (qu.matt >= 500 And qu.matt <= 503);
]
Kết quả câu lệnh:
Câu lệnh BETWEEN cũng được sử dụng trong việc giới hạn thời gian. Chẳng hạn bạn tìm kiếm các Quận huyện có ngày cập nhật (ngayud) trong khoảng thời gian từ
01-10-2018 ==> 30-10-2018  (Theo dd-MM-yyyy)
[
-- Câu lệnh này tìm kiếm các Quận huyện có ngày cập nhật trong 1 khoảng thời gian
-- xác định trong mệnh đề where.
-- 01-10-2018 ==> 30-10-2018  (Theo dd-MM-yyyy)
Select qu.maqu, qu.tenquan, qu.matt, qu.ngayud
     , -- To_Char là một hàm của Oracle.
      -- Không dùng cho DB khác.
      -- To_Char chuyển kiểu Date sang text
      -- Theo một định dạng nào đó ở đây là dd-MM-yyyy
      To_Char(qu.ngayud, 'dd-MM-yyyy') NgayCapNhat
From   mqsofthaugiang.btdquan qu
Where
-- To_Date là một hàm của Oracle (Có thể ko có trên DB khác)
-- To_Date là hàm chuyển Text thành Date
-- Theo một định dạng nào đó, ở đây là dd-MM-yyyy.
(qu.ngayud Between To_Date('01-10-2018', 'dd-MM-yyyy') And To_Date('30-10-2018', 'dd-MM-yyyy'));
]
Kết quả chạy câu lệnh:

7.8- SQL Like (Giống với ...)

Câu lệnh này chúng ta đã làm quen trên một số ví dụ ở trên.

7.9- SQL Order By (Sắp xếp bởi)

Việc query dữ liệu cho một tập kết quả, mà có thể nó sắp xếp không như ý muốn, sử dụng Order by để sắp xếp kết quả trả về.
[
-- Cú pháp
SELECT "column_name"
FROM "table_name"
[WHERE "condition"]
ORDER BY "column_name1" [ASC, DESC], "column_name2" [ASC, DESC];
-- Ghi chú:
-- ASC: nghĩa là sắp xếp tăng dần (Mặc định)
-- DESC: Nghĩa là sắp xếp giảm dần.
]
Ví dụ:
[
-- Sắp xếp ưu tiên Mã tỉnh thành tăng dần
-- Sau đó mới tới Tên quận (Cũng tăng dần)
Select qu.maqu, qu.tenquan, qu.matt
From   mqsofthaugiang.btdquan qu
Order  By qu.matt Asc, qu.tenquan  Asc;
        
        
-- Trong Order BY, ASC là mặc định.
-- Vì vậy có thể ko cần viết ASC.
Select qu.maqu, qu.tenquan, qu.matt
From   mqsofthaugiang.btdquan qu
Order  By qu.matt, qu.tenquan;
-- Sắp xếp ưu tiên Mã tỉnh thành giảm dần
-- Sau đó mới tới Tên quận (Tăng dần)
Select qu.maqu, qu.tenquan, qu.matt
From   mqsofthaugiang.btdquan qu
Order  By qu.matt Desc, qu.tenquan Asc;
]
Kết quả chạy ví dụ:
Order By bao giờ cũng đứng sau where
[
-- Tìm các Quận huyện có tên bắt đầu bởi chữ Q.
-- Sắp xếp giảm dần theo mã Quận.
Select qu.maqu, qu.tenquan, qu.matt
From   mqsofthaugiang.btdquan qu
Where  qu.tenquan Like 'Q%'
Order  By qu.maqu Desc;
]
Kết quả chạy câu lệnh:

7.10- SQL Group By (Nhóm bởi ...)

Trước hết chúng ta cần hiểu các hàm tổng hợp ( Aggregate Functions) là gì:
Sum: Hàm tính tổng
Avg: Hàm lấy trung bình
Count: Hàm tính số lần
Min: Hàm tìm giá trị nhỏ nhất
Max: Hàm tìm giá trị lớn nhất
Câu hỏi đặt ra bạn muốn xem số lượng biên lai, tổng số tiền thu được và số tiền trung bình của mỗi ngày.
[
Select To_Char(ll.ngay, 'dd-MM-yyyy') ngay,
       Count(ll.sobienlai) soluongbienlai, -- Số lượng biên lai theo ngày
       Sum(ll.sotien) Sum_sotien, -- Tổng số tiền trong ngày
       Avg(ll.sotien) Avg_sotien -- Số tiền trung bình trong ngày
From   mqsofthaugiang0419.v_ttrvll ll
Group  By To_Char(ll.ngay, 'dd-MM-yyyy')
]
Kết quả:

7.11- SQL Having

Having là một câu lệnh điều kiện của Group by.
Giả sử bạn muốn tính tổng số biên lai của các ngày thu tiền trên bảng v_ttrvll và chỉ hiển thị ra những ngày có số biên lai > 100
[
Select To_Char(ll.ngay, 'dd-MM-yyyy') ngay,
       Count(ll.sobienlai) soluongbienlai, -- Số lượng biên lai theo ngày
       Sum(ll.sotien) Sum_sotien, -- Tổng số tiền trong ngày
       Avg(ll.sotien) Avg_sotien -- Số tiền trung bình trong ngày
From   mqsofthaugiang0419.v_ttrvll ll
Group  By To_Char(ll.ngay, 'dd-MM-yyyy')
Having Count(ll.sobienlai) > 100;
]
Kết quả:
Phân biệt Where & Having
Bạn cần phân biệt Where Having trong cùng một câu lệnh.
Where là một mệnh đề để lọc bớt dữ liệu trước khi nhóm (Group)
Having là một mệnh đề để lọc bớt dữ liệu sau khi đã nhóm (Group)

8- SQL Join

Đặt ra một tình huống bạn muốn xem Tỉnh thành của một Bệnh nhân (BN) trong bảng BTDBN (Bộ từ điển Bệnh nhân). Bạn có thể thấy matt của BN này. Nhưng đó chỉ là một con số. Muốn biết tên tỉnh thành, bạn phải tra cứu sang bảng BTDTT (Danh mục Tỉnh thành). Việc nối 2 bảng đó với nhau để có một thông tin đầy đủ được gọi là JOIN.

Có 4 hình thức Join:


  • INNER JOIN   (JOIN)
  • LEFT OUTER JOIN  (LEFT JOIN)
  • RIGHT OUTER JOIN (RIGHT JOIN)
  • FULL OUTER JOIN    (OUTER JOIN)
  • CROSS JOIN

8.1- INNER JOIN (Hoặc JOIN)

INNER JOIN từ khóa chọn tất cả các dòng từ hai bảng miễn là có sự ăn khớp dữ liệu giữa các cột trong cả hai bảng.
Cú pháp:
[
SELECT column_name(s)
FROM table1
INNER JOIN table2
ON table1.column_name=table2.column_name;
-- Có thể thay INNER JOIN bởi JOIN
-- Ý nghĩa và kết quả là như nhau.
SELECT column_name(s)
FROM table1
JOIN table2
ON table1.column_name=table2.column_name;
]
Ví dụ:
[
-- INNER JOIN 2 bảng BTDBN và BTDTT.
Select bn.mabn, bn.hoten, bn.matt, tt.tentt
From   mqsofthaugiang.btdbn bn Inner  Join mqsofthaugiang.btdtt tt On bn.matt=tt.matt
Order  By bn.hoten;
]
Cú pháp riêng của Oracle:
[
-- Việc INNER JOIN theo cách viết khác của Oracle thực sự đơn giản.
-- Oracle đẩy điều kiện JOIN hai bảng xuống WHERE.
Select bn.mabn, bn.hoten, bn.matt, tt.tentt
From   mqsofthaugiang.btdbn bn, mqsofthaugiang.btdtt tt
Where bn.matt=tt.matt
Order  By bn.hoten;
]

8.2- LEFT OUTER JOIN (Hoặc LEFT JOIN)

LEFT OUTER JOIN là từ khóa trả về tất cả các hàng (rows) từ bảng bên trái (table1), với các hàng tương ứng trong bảng bên phải (table2). Chấp nhận cả dữ liệu NULL ở bảng 2.
Ví dụ:
[
-- btdbn LEFT OUTER JOIN btdtt
-- Có thể thay thế LEFT OUTER JOIN bởi LEFT JOIN (Ý nghĩa và kết quả là giống nhau)
Select bn.mabn, bn.hoten, bn.matt, tt.tentt
From   mqsofthaugiang.btdbn bn Left  Join mqsofthaugiang.btdtt tt On bn.matt=tt.matt
Order  By bn.hoten;
]
Cú pháp riêng của Oracle:
[
-- LEFT OUTER JOIN theo cú pháp khác của Oracle.
-- Oracle đẩy điều kiện LEFT OUTER JOIN hai bảng xuống WHERE.
-- Sử dụng (+) tại Table2.
Select bn.mabn, bn.hoten, bn.matt, tt.tentt
From   mqsofthaugiang.btdbn bn -- Table1
, mqsofthaugiang.btdtt tt -- Table2
Where bn.matt=tt.matt(+)
Order  By bn.hoten;
]

8.3- RIGHT OUTER JOIN (Hoặc RIGHT JOIN)

RIGHT OUTER JOIN ngược lại với LEFT OUTER JOIN:

8.4- FULL OUTER JOIN (Hoặc OUTER JOIN)

FULL OUTER JOIN là sự kết hợp của LEFT OUTER JOINRIGHT OUTER JOIN
[
-- Cú pháp:  (FULL OUTER JOIN)
-- Có thể viết FULL JOIN
SELECT columns
FROM table1
FULL [OUTER] JOIN table2
ON table1.column = table2.column;
]

9- Câu truy vấn con (Subquery)

Trong Oracle, một subquery là một truy vấn trong một truy vấn. Bạn có thể tạo các truy vấn con trong câu lệnh SQL của bạn. Những truy vấn con có thể nằm trong mệnh đề WHERE, mệnh đề FROM, hoặc mệnh đề SELECT.

9.1- Subquery trong mệnh đề Where

Khá thường xuyên, các subquery sẽ được tìm thấy trong mệnh đề WHERE. Những truy vấn con còn được gọi là truy vấn con lồng nhau.
[
Select Acc.Account_Id
     ,Acc.Open_Date
     ,Acc.Product_Cd
     ,Acc.Avail_Balance
From   Account Acc
Where  Acc.Cust_Id In
      (Select Cus.Cust_Id From Customer Cus Where Cus.Cust_Type_Cd = 'B')
]

9.2- Subquery trong mệnh đề From

Một truy vấn phụ cũng có thể được tìm thấy trong mệnh đề FROM. Chúng được gọi là inline views.
[
Select Cus.Cust_Id
     ,Cus.Address
     ,Cus.Fed_Id
     ,Acc2.Sum_Avail_Balance
From   Customer Cus
     , -- Câu Subquery định nghĩa 1 bảng ảo (inline view)
      (Select Acc.Cust_Id
             ,Sum(Acc.Avail_Balance) As Sum_Avail_Balance
       From   Account Acc
       Group  By Acc.Cust_Id) Acc2
Where  Cus.Cust_Id = Acc2.Cust_Id;
]

9.3- Subquery trong mệnh đề Select

Một truy vấn phụ cũng có thể được tìm thấy trong mệnh đề SELECT.
[
Select Cus.Cust_Id
     ,Cus.Address
     ,Cus.Fed_Id
     ,(Select Sum(Acc.Avail_Balance)
       From   Account Acc
       Where  Acc.Cust_Id = Cus.Cust_Id) As Sum_Avail_Balance
From   Customer Cus;
]
Bí quyết để đặt một truy vấn phụ trong mệnh đề select là subquery phải trả lại một giá trị duy nhất. Đây là lý do tại sao một các hàm tổng hợp như hàm SUM, COUNT, MIN, hoặc MAX thường được sử dụng trong subquery.

















COMMENTS

Name

Oracle,2,PL/SQL Developer,1,
ltr
item
MQ Solution Learning: Hướng dẫn học SQL cho người mới bắt đầu với Oracle
Hướng dẫn học SQL cho người mới bắt đầu với Oracle
https://o7planning.org/vi/10231/cache/images/i/22766.png
https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQTq73YrM25VKgkbvOj-1A__VGu6blXxyTftCf1J40xH8CG1nJM-4BexeYfS9p4MjKDRG5rMNBmRyJuVtN5yj_XJxuBGLQIxSYNQ3vF583Qd5DD7MkrWvZEc4AnLtTNa4l7RAk2Fft8-4/s72-c/2019-05-06_08h34_04.png
MQ Solution Learning
https://mqlearning.blogspot.com/2019/05/huong-dan-hoc-sql-cho-nguoi-moi-bat-au.html
https://mqlearning.blogspot.com/
https://mqlearning.blogspot.com/
https://mqlearning.blogspot.com/2019/05/huong-dan-hoc-sql-cho-nguoi-moi-bat-au.html
true
6215337335754463418
UTF-8
Loaded All Posts Not found any posts VIEW ALL Readmore Reply Cancel reply Delete By Home PAGES POSTS View All RECOMMENDED FOR YOU LABEL ARCHIVE SEARCH ALL POSTS Not found any post match with your request Back Home Sunday Monday Tuesday Wednesday Thursday Friday Saturday Sun Mon Tue Wed Thu Fri Sat January February March April May June July August September October November December Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec just now 1 minute ago $$1$$ minutes ago 1 hour ago $$1$$ hours ago Yesterday $$1$$ days ago $$1$$ weeks ago more than 5 weeks ago Followers Follow THIS PREMIUM CONTENT IS LOCKED STEP 1: Share to a social network STEP 2: Click the link on your social network Copy All Code Select All Code All codes were copied to your clipboard Can not copy the codes / texts, please press [CTRL]+[C] (or CMD+C with Mac) to copy