Đố vui về VBA! (2 người xem)

Liên hệ QC

Người dùng đang xem chủ đề này

anhtuan1066

Thành viên gạo cội
Tham gia
10/3/07
Bài viết
5,802
Được thích
6,912
Nhằm cũng cố kiến thức về VBA cho các bạn mới bắt đầu và cả những bạn đang ứng dụng mà chưa hiểu nhiều về nó, tôi mở topic này với mong mõi qua những câu hỏi vui, các bạn sẽ nhận định lại sự hiểu biết cũa mình... (Kễ cã chính tôi cũng đang tập tành nên có rất nhiều cái chưa biết)
Mong rằng topic sẽ mang đến cho các bạn những khám phá thú vị với những cái tưỡng chừng như đã biết
Mong nhận dc bài viết về câu đố cũa các cao thủ! Còn các bạn mới thì đừng ngại khi đưa ra ý kiến cũa mình.. Có sai có sữa sẽ hoàn thiện!
Tôi xin mỡ màn trước bằng 1 câu hỏi đơn giãn
ANH TUẤN

CÂU HỎI 1: Tại sao biến K ko hoạt động?
Tôi muốn khi nhấn vào 1 button thì cell A1 sẽ tăng lên 1 đơn vị... Tôi đã làm như sau:
-Tạo 1 Command Button (nút nhấn thuộc thanh Control Toolbox), click phải chuột lên nút nhấn, chọn View code, rồi gõ vào đoạn code sau:
PHP:
Private Sub CommandButton1_Click()
   K = K + 1
   Range("A1").Value = K
End Sub
Ban đầu K chưa có gì, xem như =0, nhấn nút lần thứ nhất thì K dc tăng thêm 1, vậy K hiện tại sẽ bằng 1, và gán K vào cell A1 thì đương nhiên A1 sẽ =1... Nhấn nút lần 2, K lại dc tăng thêm 1 nên hiện tại K sẽ =2 và cell A1 cũng sẽ =2... vân vân.. từ đó diễn tiến tiếp...
Hi.. hi.. Điều này nghe qua có vẽ rất hợp lý, ấy thế mà khi nhấn nút nó chỉ hoạt động dc duy nhất 1 lần (A1 = 1) rồi thôi ko nhút nhít nữa...
Các bạn có thể giãi thích tại sao lại như thế ko? Tại sao những lần nhấn nút sau đó K lại ko tăng thêm tí nào (vì thực tế A1 vẫn cứ = 1 hoài) ?
ANH TUẤN
 
cuối năm giải quyết hàng tồn còn đi ăn tết , ý anh là đếm coi có bao nhiêu process đang chạy có tên là excel.exe ? vậy ta dùng lệnh

Mã:
GetObject("winmgmts:").ExecQuery("Select * from Win32_Process Where Name = 'EXCEL.EXE'").Count
Có phải làm gì nữa không anh? Chứ em chạy dòng đó bị như hình

Excel.jpg
 
Upvote 0
Câu đố này có lẽ không vui lắm. Nhưng cứ đố đại:

Tôi có một Project, trong đó có cái moduleX, với code như sau:

Option Explicit

Sub t1()
a = 1
msgbox a
End Sub

Sub t2()
Dim a
a = 1
msgbox a
End Sub

Sub t3()
Dim a As Integer
a = 1
msgbox a
End Sub

Câu hỏi: 3 cái sub này khác nhau như thế nào?
 
Upvote 0
Câu đố này có lẽ không vui lắm. Nhưng cứ đố đại:

Tôi có một Project, trong đó có cái moduleX, với code như sau:

Option Explicit

Sub t1()
a = 1
msgbox a
End Sub

Sub t2()
Dim a
a = 1
msgbox a
End Sub

Sub t3()
Dim a As Integer
a = 1
msgbox a
End Sub

Câu hỏi: 3 cái sub này khác nhau như thế nào?

thưa anh , sub t1() khác t2() và t3() ở chỗ không có dòng Dim a
Sub t2() khác t3() ở chỗ không có chữ As Integer --=0--=0
 
Upvote 0
Vì có câu lệnh Option, nên Sub T1() sẽ báo lỗi.
 
Upvote 0
ủa cái này là thầy giáo đang khen hay chửi học trò vậy ? híc +-+-+-++-+-+-+

Khi Bồ Đề Lão Tổ gõ vào đầu con khỉ họ Tôn 3 cái thì là khen hay chửi?

Lần này thì chê: nếu chỉ biết nhìn câu nói trên quan điểm khen hay chửi thì chí học tập kém quá.
 
Upvote 0
Câu đố này có lẽ không vui lắm. Nhưng cứ đố đại:

Tôi có một Project, trong đó có cái moduleX, với code như sau:
...
Câu hỏi: 3 cái sub này khác nhau như thế nào?

(Không biết có giống với bài chứng minh: 1+1=2 không anh?)
Khác: Không và có khai báo biến a, và cách khai báo biến a.

Nếu a=1 thì dùng kiểu byte,
Mã:
Option Explicit
Sub t4()
dim a as byte
a = 1
msgbox a
End Sub

và nếu chỉ có 1 giá trị =1 không đổi thì
Mã:
Option Explicit
const a as byte = 1
Sub t5()
msgbox a
End Sub
 
Upvote 0
(Không biết có giống với bài chứng minh: 1+1=2 không anh?)
Khác: Không và có khai báo biến a, và cách khai báo biến a.

Nếu a=1 thì dùng kiểu byte,

Khai báo biến: nóng (gần ý tôi rồi. Thử suy diễn thêm nữa xem)
Dùng kiểu byte: nguội ngắt (đối với tôi, byte và integer khác nhau chỉ 3 bytes là cùng, không đáng gọi là tiền lẻ, trừ phi bạn có mảng hàng trăm ngàn phần tử)

Chú: giá trj 1 chỉ là một ví dụ biến được sử dụng. Bạn có thể cho a = CInt([a1].value)
 
Upvote 0
Khi Bồ Đề Lão Tổ gõ vào đầu con khỉ họ Tôn 3 cái thì là khen hay chửi?

Lần này thì chê: nếu chỉ biết nhìn câu nói trên quan điểm khen hay chửi thì chí học tập kém quá.

gõ đầu 3 cái không phải để khen hay chửi , mà để kiểm tra độ cứng của cái đầu --=0--=0
em đang mệt bỗng được nghe anh Vetmini chê thì tự nhiên thấy tươi tỉnh trở lại ngay , cám ơn anh .
Giờ ta nói lại về câu hỏi nhé
Với sự khác nhau về câu chữ giữa 3 sub đã nêu ở trên (như kiểu con cá khác con mực vì nó có xương ) , sự khác nhau này gây ra hậu quả gì ?

sub t1() không khai báo biến đồng thời có lệnh Option Explicit
Thủ tục này sẽ chạy khi vô tình có 1 biến toàn cục nào đó có tên là a , ngược lại sẽ báo lỗi

Sub t3() có khai báo biến a là kiểu Integer , khi biên dịch , hệ thống sẽ cấp cho a 1 vùng nhớ thích hợp , và cố định dành cho kiểu Integer

Sub t2() khai báo a , nhưng không xác định kiểu , trình biên dịch tự hiểu biến a mang kiểu mặc định là Variant . Vì không biết a là "cái giống gì" , nên hệ thống không thể cấp trước cho a 1 vùng nhớ cố định , hệ quả là trong khi code đang chạy , mỗi lần gặp lệnh gán

Mã:
a = Some value

trình biên dịch mới thực hiện động tác xem coi biến a là "cái giống gì" , từ đó mới cấp cho a 1 vùng nhớ thích hợp .

đấy là những suy nghĩ nông cạn của em , mong anh góp ý .
Mấy bữa trước nghe nói anh bị bệnh nan y , thật em cũng buồn , sợ thiếu mất người chửi mình , nay thấy anh vẫn khỏe mạnh , em rất vui
--=0--=0
Chúc anh nhiều sức khỏe , luôn đồng hành cùng diễn đàn .
 
Upvote 0
Đúng gần hết rồi. Suy diễn thêm chút nữa đi.

Gợi ý 1: nhiệm vụ của cái câu Option Explicit

Gợi ý 2: kết nối sớm và kết nối trễ

trình biên dịch mới thực hiện động tác xem coi biến a là "cái giống gì" , từ đó mới cấp cho a 1 vùng nhớ thích hợp


Không hoàn toàn sai. Nhưng động tác này xảy ra lúc chạy chứ không phải lúc biên dịch -> kết nối trễ
 
Upvote 0
Đúng gần hết rồi. Suy diễn thêm chút nữa đi.

Gợi ý 1: nhiệm vụ của cái câu Option Explicit

Gợi ý 2: kết nối sớm và kết nối trễ



Không hoàn toàn sai. Nhưng động tác này xảy ra lúc chạy chứ không phải lúc biên dịch -> kết nối trễ
[/COLOR]

thử lại lần nữa vậy , lệnh Option Explicit làm cho trình biên dịch phải thực hiện kết nối sớm bao nhiêu có thể , nếu không có Option Explicit thì sub t1() và t2() là như nhau , đều là kết nối trễ . --=0--=0
 
Upvote 0
Biến toàn cục nằm trong vùng nhớ heap (vùng nhớ chính), biến cục bộ nằm trong vùng nhớ stack (vùng nhớ thuộc về hàm/sub)
Khi khởi chạy Project là các biến toàn cục đã được thành lập rồi. Việc thành lập khong liên quan đến kết nối sớm/trễ. Việc định dạng mới liên quan đến kết nối.

Minh hoạ kêt nói trễ và sớm như sau: (minh hoạ để dễ hiểu chứ không phải để giải thích kỹ thuật, bởi vì kỹ thuật thực hiện còn tuỳ theo ngôn ngữ và hệ điều hành)
Các biến kết nối sớm, ví dụ như Integer được trình dịch cho vùng nhớ ngay lúc Dim. Khi gặp lệnh gán (dấu =) thì chương trình chỉ việc chép kết quả của biểu thức bên phải dấu gán vào vùng nhớ đó. String tuy không có vùng nhớ xác định, nhưng trình dịch cũng đã cho nó vùng chỉ định dạng của nó rồi, cho nên cũng có thể coi như kết nối sớm
Các biến variant được trình dịch cho vào bảng kết nối trễ. Lúc đó nó chỉ có tên chứ chưa có dạng. Đến lúc được gán trị nó mới được trỏ vào cái kết quả của biểu thức bên phải dấu gán (thường là một cóp py của cái kết quả này). Và vì vậy nó mang kiểu của cái nó trỏ vào, cho đến khi có lệnh gán trỏ nó vào kiểu khác.

Điều trên giải thích điểm khác nhau giữa sub t2() và t3(). Nói thẳng ra, biến a trong t3() bắt buộc phải là Integer, biến a trong t2() có thể là cái gì cũng được.

Như vậy, khi tôi đọc code, cần debug code hay phát triển thêm code, tôi có thể dựa trên đặc tính trên để khẳng định rằng ngừoi viết code muốn xác định trước nhiệm vụ của a trong t3() luôn luôn là Integer, và a trong t2() thì chưa xác định, có lúc là kiểu này nhưng cũng có lúc kiểu khác.

Ba cái subs trên khác nhau không phải do viết theo kiểu lười biếng, mà chúng có lý do của chúng. Nếu có sự lười biếng thì chỉ là lười biếng đặt tên (tên 'a' chả minh bạch chút nào)

Nhiệm vụ của 'Option Explicit':
Vì có lệnh này, khi đọc qua t1(), tôi nhận ra ngay a là biến toàn cục được khai báo Public ở 1 moduleY hay Z nào đó. Bởi vì nếu không phải biến toàn cục, trình dịch còn lâu mới chấp nhận.
Nếu khong có lệnh này thì tôi không thể biết ngay rằng đây là biến toàn cục hay không. Néu quên dùng lệnh Find để tìm khắp các module thì có mà chết.
Khi nhận ra nó là biến toàn cục thì tôi cũng đồng thời biết rằng t1() không phải là hàm độc lập. Khi chạy nó có nhiều khả năng ảnh hưởng đến các hàm khác dùng chung biến này. Và mỗi lần chạy chưa chắc nó ra kết quả giống nhau.
Ví dụ bạn thay lệnh a = 1 với
a = a + 1
Mỗi lượt chạy t1() sẽ cho bạn kết quả khác.

Tóm lại, cái khác nhau giữa 3 cái hàm trên không hẳn nằm ở chỗ người viết lười hay siêng "khai báo biến tường minh". Ngừoi viết code này có dụng ý riêng cho mỗi loại khai báo.
 
Lần chỉnh sửa cuối:
Upvote 0
Các bạn xem clip bên dưới nhé:

[video=youtube;O8F5NNdRVfg]https://www.youtube.com/watch?v=O8F5NNdRVfg&feature=youtu.be[/video]
 
Upvote 0
Các bạn xem clip bên dưới nhé:

[video=youtube;O8F5NNdRVfg]https://www.youtube.com/watch?v=O8F5NNdRVfg&feature=youtu.be[/video]

ở trên người ta có nói rồi mà

cuối năm giải quyết hàng tồn còn đi ăn tết , ý anh là đếm coi có bao nhiêu process đang chạy có tên là excel.exe ? vậy ta dùng lệnh



Mã:
GetObject("winmgmts:").ExecQuery("Select * from Win32_Process Where Name = 'EXCEL.EXE'").Count

vậy xin anh Hai Lúa Miền Tây cho biết sự khác nhau "winmgmts:" và "winmgmts:root\CIMV2"
 
Upvote 0
1. ở trên người ta có nói rồi mà



2. vậy xin anh Hai Lúa Miền Tây cho biết sự khác nhau "winmgmts:" và "winmgmts:root\CIMV2"

1. Xin lỗi tôi đã thấy câu trả lời này ở trên, nên tôi đưa đáp án của tôi ra mà thôi.
2. Tôi chỉ biết vận dung, còn để phân tích thì với trình độ của tôi không thể trả lời cho bạn được.
 
Upvote 0
ExecQuery là 1 phương thức nằm trong cái không gian danh phận (namespace) của \root\CIMV2\
namespace là vùng dùng để phân biệt tên biến, thuộc tính, và phương thức (cũng như folders, 2 files tên giống nhau không thể nằm cùng folder, nhưng khác folder thì vô tư)
Trong trường hợp này, winmgmts không có phương thức nào khác trùng tên cho nên scripting engine tự động nối tiền tố mặc định cho phương thức. Ta có thể nói, có hay không có cũng được.
Lưu ý là winmgmts có thể được gọi cho máy khác cùng mạng (nếu script được cho phép).

@AutoReply:
"gõ đầu 3 cái không phải để khen hay chửi , mà để kiểm tra độ cứng của cái đầu --=0--=0"
Lúc đưa ra câu đố "không được quẳng lên trời, không liệng xuống đất" thì Bồ đề đã kiểm tra Hầu vương xong rồi (tuy theo quan niệm triết mà người có thể lý luận đây là kiểm tra trí tuệ hay nhân phẩm)
Khi gõ đầu, Bồ đề không cần kiểm tra Hầu vương thêm nữa. Nếu chịu khó đọc truyện cho kỹ thì đã biết đấy là một ám hiệu.
 
Upvote 0
Cuối tuần giải trí chút nha các bạn.
Nhờ các bạn lấy dữ liệu từ file đang đóng data.xlsx tại những dòng có stt nằm giữa 100 và 1000
Mà không sử dụng các công cụ Vòng lặp , Filter , ADO
Khuyến khích "chơi thế lạ"
Cảm ơn các bạn
 

File đính kèm

Upvote 0
Cuối tuần giải trí chút nha các bạn.
Nhờ các bạn lấy dữ liệu từ file đang đóng data.xlsx tại những dòng có stt nằm giữa 100 và 1000
Mà không sử dụng các công cụ Vòng lặp , Filter , ADO
Khuyến khích "chơi thế lạ"
Cảm ơn các bạn
Mình đưa tạm đáp án bạn xem có được không nhé
Mã:
Sub GetData()
    Dim Wb As Workbook
    Dim Sh As Worksheet
    Dim Rng As Range
    Dim Link As String
   
    Link = "D:\data.xlsx"
    Set Wb = Application.Workbooks.Open(Link)
    Set Sh = Wb.Sheets("sheet1")
    Sh.[C2:C100001].FormulaR1C1 = "=IF(AND(RC[-2]>100,RC[-2]<900),1,""NG"")"
    Set Rng = Sh.[C2:C100001].SpecialCells(xlCellTypeFormulas, 1)
    Range(Rng.Offset(, -2), Rng.Offset(, -1)).Copy
    ThisWorkbook.ActiveSheet.Range("A2").PasteSpecial
   
End Sub

Hình như bài này chơi MS Query cũng được nhỉ?
 
Upvote 0
Cuối tuần giải trí chút nha các bạn.
Nhờ các bạn lấy dữ liệu từ file đang đóng data.xlsx tại những dòng có stt nằm giữa 100 và 1000
Mà không sử dụng các công cụ Vòng lặp , Filter , ADO
Khuyến khích "chơi thế lạ"
Cảm ơn các bạn
Dùng Macro4 hay dùng hàm vậy bạn?
Tôi dùng hàm thử:
PHP:
Sub LayDL()
    With Range("A100:B1000")
        .Formula = "='" & ThisWorkbook.path & "\[" & "Data.xlsx" & " ]" & "Sheet1" & "'!" & "A99:B1000"
        .Value = .Value
    End With
    Range("A2:B99").EntireRow.Delete
    
End Sub
Là do bạn không nói stt có thể không liên tục.
 
Upvote 0
Cuối tuần giải trí chút nha các bạn.
Nhờ các bạn lấy dữ liệu từ file đang đóng data.xlsx tại những dòng có stt nằm giữa 100 và 1000
Mà không sử dụng các công cụ Vòng lặp , Filter , ADO
Khuyến khích "chơi thế lạ"
Cảm ơn các bạn
Dùng lại code của chị bữa hổm:
PHP:
Sub getdata()
    spath = "='D:\[data.xlsx]Sheet1'!A100"
    [a1].Formula = spath: [a1:b1].FillRight: Range("a1:b" & 1000 - 100 + 1).FillDown
    Range("a1:b" & 1000 - 100 + 1).Value = Range("a1:b" & 1000 - 100 + 1).Value
End Sub
 
Upvote 0
Dùng Macro4 hay dùng hàm vậy bạn?
Tôi dùng hàm thử:
PHP:
Sub LayDL()
    With Range("A100:B1000")
        .Formula = "='" & ThisWorkbook.path & "\[" & "Data.xlsx" & " ]" & "Sheet1" & "'!" & "A99:B1000"
        .Value = .Value
    End With
    Range("A2:B99").EntireRow.Delete
   
End Sub
Là do bạn không nói stt có thể không liên tục.

Đố vui mà bạn, cứ ra kết quả là được hết. Cám ơn bạn. :):)
 
Upvote 0
Cuối tuần giải trí chút nha các bạn.
Nhờ các bạn lấy dữ liệu từ file đang đóng data.xlsx tại những dòng có stt nằm giữa 100 và 1000
Mà không sử dụng các công cụ Vòng lặp , Filter , ADO
Khuyến khích "chơi thế lạ"
Cảm ơn các bạn
Cũng nghịch ngợm tí tẹo:
PHP:
Sub hibe()
Dim s1, s2, n: s1 = 100: s2 = 1000: n = "titeo"
With Sheet1
    .Names.Add Name:=n, RefersTo:="='" & ThisWorkbook.Path & "\[data.xlsx]Sheet1'!$A$" & s1 + 1 & ":$B$" & s2 - 1
    .Range("A2:B" & s2 - s1).FormulaArray = "=titeo"
    .Range("A2:B" & s2 - s1).Value = Range("A2:B" & s2 - s1).Value
    .Names(n).Delete
End With
End Sub

@eke_rula : Lại thêm vụ xác định "giới tính" :p:p
 
Upvote 0
Hình như bài này chơi MS Query cũng được nhỉ?

Các bạn kia "chơi ăn gian" quá nhỉ, nhưng mình thích sự nhiệt tình của các bạn .
DHN à, bạn có nghiên cứu về mấy cái MS query không ? Mình mới thử nghiệm vài thí dụ về nó nhưng thấy tốc độ trả về rất chậm.
Nếu được bạn xem thử giúp mình đoạn này mình có bị thiếu ở đâu không mà tốc độ rất chậm . Cảm ơn bạn nhiều.

Mã:
Public Sub hello()
Dim Link As String, lo As ListObject
Link = ThisWorkbook.Path & "\data.xlsx"
ThisWorkbook.Queries.Add "myquery", "let " & _
" Source = Excel.Workbook(File.Contents(""" & Link & """), null, true), " & _
" Sheet1_Sheet = Source{[Item=""Sheet1"",Kind=""Sheet""]}[Data], " & _
" Headers = Table.PromoteHeaders(Sheet1_Sheet), " & _
" filter = Table.SelectRows(Headers, each [stt] > 100 and [stt] < 1000) in filter"

Set lo = Sheet1.ListObjects.Add(xlSrcExternal, "OLEDB;Provider=Microsoft.Mashup.OleDb.1;" & _
"Data Source=$Workbook$;Location=myquery", Destination:=Sheet1.[E2])
lo.TableStyle = ""
With lo.QueryTable
    .CommandText = "SELECT * FROM [myquery]"
    .Refresh BackgroundQuery:=False
End With
lo.Unlist
ThisWorkbook.Queries("myquery").Delete
End Sub

Lưu ý đoạn code trên chỉ chạy được với Excel 2016, các phiên bản trước có thể sẽ không được vì không có

Mã:
Microsoft.Mashup.OleDb.1
 
Upvote 0
Các bạn kia "chơi ăn gian" quá nhỉ, nhưng mình thích sự nhiệt tình của các bạn .
DHN à, bạn có nghiên cứu về mấy cái MS query không ? Mình mới thử nghiệm vài thí dụ về nó nhưng thấy tốc độ trả về rất chậm.
Nếu được bạn xem thử giúp mình đoạn này mình có bị thiếu ở đâu không mà tốc độ rất chậm . Cảm ơn bạn nhiều.

Mã:
Public Sub hello()
Dim Link As String, lo As ListObject
Link = ThisWorkbook.Path & "\data.xlsx"
ThisWorkbook.Queries.Add "myquery", "let " & _
" Source = Excel.Workbook(File.Contents(""" & Link & """), null, true), " & _
" Sheet1_Sheet = Source{[Item=""Sheet1"",Kind=""Sheet""]}[Data], " & _
" Headers = Table.PromoteHeaders(Sheet1_Sheet), " & _
" filter = Table.SelectRows(Headers, each [stt] > 100 and [stt] < 1000) in filter"

Set lo = Sheet1.ListObjects.Add(xlSrcExternal, "OLEDB;Provider=Microsoft.Mashup.OleDb.1;" & _
"Data Source=$Workbook$;Location=myquery", Destination:=Sheet1.[E2])
lo.TableStyle = ""
With lo.QueryTable
    .CommandText = "SELECT * FROM [myquery]"
    .Refresh BackgroundQuery:=False
End With
lo.Unlist
ThisWorkbook.Queries("myquery").Delete
End Sub

Lưu ý đoạn code trên chỉ chạy được với Excel 2016, các phiên bản trước có thể sẽ không được vì không có

Mã:
Microsoft.Mashup.OleDb.1
kể cũng lạ chạy lần đầu tiên nó dùng giật quay vòng mấy cái mới ra ... lần 2 trở đi sao nó ra nhanh vậy he
Món này keo là gì vậy Bạn ...?!
 
Upvote 0
Chào bạn AutoReply mình không có nghiên cứu về Ms Query bạn ah.
 
Upvote 0
Ngồi thử QueryTable trên Excel 2010 cũng ra đáp án của bạn, thôi thì góp vui chứ hổng có hiểu gì hết +-+-+-++-+-+-++-+-+-+
Mã:
Sub GetData()
    With ActiveSheet.ListObjects.Add(0, Source:= _
        "ODBC;DSN=Excel Files;DBQ=D:\data.xlsx;DefaultDir=D:;DriverId=1046;MaxBufferSize=2048;PageTimeout=5;" _
       , Destination:=Range("$A$1")).QueryTable
        .CommandText = "SELECT * FROM `D:\data.xlsx`.`Sheet1$` WHERE [stt] > 100 and [stt] < 1000"
        .Refresh
    End With
End Sub
 
Upvote 0
Ngồi thử QueryTable trên Excel 2010 cũng ra đáp án của bạn, thôi thì góp vui chứ hổng có hiểu gì hết +-+-+-++-+-+-++-+-+-+
Mã:
Sub GetData()
    With ActiveSheet.ListObjects.Add(0, Source:= _
        "ODBC;DSN=Excel Files;DBQ=D:\data.xlsx;DefaultDir=D:;DriverId=1046;MaxBufferSize=2048;PageTimeout=5;" _
       , Destination:=Range("$A$1")).QueryTable
        .CommandText = "SELECT * FROM `D:\data.xlsx`.`Sheet1$` WHERE [stt] > 100 and [stt] < 1000"
        .Refresh
    End With
End Sub
Cái này office 2016 - Windows10 nó ra như hìnhCapture.PNG
 
Upvote 0
Ngồi thử QueryTable trên Excel 2010 cũng ra đáp án của bạn, thôi thì góp vui chứ hổng có hiểu gì hết +-+-+-++-+-+-++-+-+-+
Mã:
Sub GetData()
    With ActiveSheet.ListObjects.Add(0, Source:= _
        "ODBC;DSN=Excel Files;DBQ=D:\data.xlsx;DefaultDir=D:;DriverId=1046;MaxBufferSize=2048;PageTimeout=5;" _
       , Destination:=Range("$A$1")).QueryTable
        .CommandText = "SELECT * FROM `D:\data.xlsx`.`Sheet1$` WHERE [stt] > 100 and [stt] < 1000"
        .Refresh
    End With
End Sub
Có thể chỉnh lại như sau:
Mã:
Sub GetData()
    With ActiveSheet.ListObjects.Add(0, Source:="ODBC;DSN=Excel Files;DBQ=" & ThisWorkbook.path & "\data.xlsx;", Destination:=Range("$C$1")).QueryTable
        .CommandText = "SELECT * FROM [Sheet1$] WHERE [stt] BETWEEN 100 and 1000"
        .Refresh
    End With
End Sub
 
Lần chỉnh sửa cuối:
Upvote 0
Cái này mình không có Excel 2016 để Test, có thể cấu trúc lệnh 2010 và 2016 có khác nhau chút chút. Bạn thử Record macro xem khác điểm nào rồi post cho mình tham khảo nhé ^^.
Record macro office 2016 nó ra vầy
Mã:
Sub Macro1()
'
' Macro1 Macro
'

'
    ActiveWorkbook.Queries.Add Name:="Sheet1", Formula:= _
        "let" & Chr(13) & "" & Chr(10) & "    Source = Excel.Workbook(File.Contents(""D:\dovui\data.xlsx""), null, true)," & Chr(13) & "" & Chr(10) & "    Sheet1_Sheet = Source{[Item=""Sheet1"",Kind=""Sheet""]}[Data]," & Chr(13) & "" & Chr(10) & "    #""Promoted Headers"" = Table.PromoteHeaders(Sheet1_Sheet)," & Chr(13) & "" & Chr(10) & "    #""Changed Type"" = Table.TransformColumnTypes(#""Promoted Headers"",{{""stt"", Int64.Type}, {""gt"", type text}})" & Chr(13) & "" & Chr(10) & "in" & Chr(13) & "" & Chr(10) & "    #""Changed Type"""
    Sheets.Add After:=ActiveSheet
    With ActiveSheet.ListObjects.Add(SourceType:=0, Source:= _
        "OLEDB;Provider=Microsoft.Mashup.OleDb.1;Data Source=$Workbook$;Location=Sheet1" _
        , Destination:=Range("$A$1")).QueryTable
        .CommandType = xlCmdSql
        .CommandText = Array("SELECT * FROM [Sheet1]")
        .RowNumbers = False
        .FillAdjacentFormulas = False
        .PreserveFormatting = True
        .RefreshOnFileOpen = False
        .BackgroundQuery = True
        .RefreshStyle = xlInsertDeleteCells
        .SavePassword = False
        .SaveData = True
        .AdjustColumnWidth = True
        .RefreshPeriod = 0
        .PreserveColumnInfo = False
        .ListObject.DisplayName = "Sheet1"
        .Refresh BackgroundQuery:=False
    End With
    Selection.ListObject.QueryTable.Refresh BackgroundQuery:=False
    Range("H16").Select
End Sub
Còn thử Nhấn OK xem sao nó Báo lỗi .Refresh .... Mạnh bỏ luôn .Refresh ...nó không ra gì cả
 
Upvote 0
Record macro office 2016 nó ra vầy
Mã:
Sub Macro1()
'
' Macro1 Macro
'

'
    ActiveWorkbook.Queries.Add Name:="Sheet1", Formula:= _
        "let" & Chr(13) & "" & Chr(10) & "    Source = Excel.Workbook(File.Contents(""D:\dovui\data.xlsx""), null, true)," & Chr(13) & "" & Chr(10) & "    Sheet1_Sheet = Source{[Item=""Sheet1"",Kind=""Sheet""]}[Data]," & Chr(13) & "" & Chr(10) & "    #""Promoted Headers"" = Table.PromoteHeaders(Sheet1_Sheet)," & Chr(13) & "" & Chr(10) & "    #""Changed Type"" = Table.TransformColumnTypes(#""Promoted Headers"",{{""stt"", Int64.Type}, {""gt"", type text}})" & Chr(13) & "" & Chr(10) & "in" & Chr(13) & "" & Chr(10) & "    #""Changed Type"""
    Sheets.Add After:=ActiveSheet
    With ActiveSheet.ListObjects.Add(SourceType:=0, Source:= _
        "OLEDB;Provider=Microsoft.Mashup.OleDb.1;Data Source=$Workbook$;Location=Sheet1" _
        , Destination:=Range("$A$1")).QueryTable
        .CommandType = xlCmdSql
        .CommandText = Array("SELECT * FROM [Sheet1]")
        .RowNumbers = False
        .FillAdjacentFormulas = False
        .PreserveFormatting = True
        .RefreshOnFileOpen = False
        .BackgroundQuery = True
        .RefreshStyle = xlInsertDeleteCells
        .SavePassword = False
        .SaveData = True
        .AdjustColumnWidth = True
        .RefreshPeriod = 0
        .PreserveColumnInfo = False
        .ListObject.DisplayName = "Sheet1"
        .Refresh BackgroundQuery:=False
    End With
    Selection.ListObject.QueryTable.Refresh BackgroundQuery:=False
    Range("H16").Select
End Sub
Còn thử Nhấn OK xem sao nó Báo lỗi .Refresh .... Mạnh bỏ luôn .Refresh ...nó không ra gì cả
Anh sửa thành SELECT * FROM [Sheet1$] xem được không?
 
Upvote 0
Tôi có một câu đố nho nhỏ. Đúng nghĩa đó vui luôn :)
Viết code copy giá trị và định dạng từ A1:A10 sang C1:C10 sao cho code gọn nhất.
 
Upvote 0
Vậy phải copy thêm định dạng qua nữa hả anh?
PHP:
Sub vidu()
Range("A1:A10").Copy
With Range("C1:C10")
    .PasteSpecial xlPasteValues  'Ý 1: Lấy giá trị
    .PasteSpecial xlPasteFormats  'Ý 2: Lấy định dạng
    '.PasteSpecial xlPasteFormulasAndNumberFormats 'Nếu đề bài chỉ lấy định dạng số thì dòng này là được :D
End With
Application.CutCopyMode = False
End Sub
 
Upvote 0
PHP:
Sub vidu()
Range("A1:A10").Copy
With Range("C1:C10")
    .PasteSpecial xlPasteValues  'Ý 1: Lấy giá trị
    .PasteSpecial xlPasteFormats  'Ý 2: Lấy định dạng
    '.PasteSpecial xlPasteFormulasAndNumberFormats 'Nếu đề bài chỉ lấy định dạng số thì dòng này là được :D
End With
Application.CutCopyMode = False
End Sub
Dài quá :D
 
Upvote 0
Rút lại nhưng vẫn còn dài

[a1:A10].Copy
[c1].PasteSpecial 12
Selection.PasteSpecial -4163
 
Upvote 0
Ngồi buồn thử đố quý vị. Code hết sức đơn giản.

code 1
For i = 1 to giaTriCuoi
' code gì đó
if (dk) then Exit For
Next i

code 2
For i = 1 to giaTriCuoi
' code gì đó
if (dk) then i = giaTriCuoi
Next i

Hai code trên khác nhau chỗ nào (dễ rồi). Khi nào tôi dùng 2 mà không dùng 1? (gợi ý: câu trước không hẳn dễ lắm đâu)
 
Upvote 0
Upvote 0
Ngồi buồn thử đố quý vị. Code hết sức đơn giản.

code 1
For i = 1 to giaTriCuoi
' code gì đó
if (dk) then Exit For
Next i

code 2
For i = 1 to giaTriCuoi
' code gì đó
if (dk) then i = giaTriCuoi
Next i

Hai code trên khác nhau chỗ nào (dễ rồi). Khi nào tôi dùng 2 mà không dùng 1? (gợi ý: câu trước không hẳn dễ lắm đâu)
Theo tôi thì:
1. Điểm khác nhau là giá trị của i khi thoát vòng lặp. Với code 1 i sẽ là giá trị khi dk = True, code 2 thì giá trị của i giống như khi vòng lặp được duyệt hết, bằng giaTriCuoi + 1.
2. Nếu muốn sử dụng tiếp biến i và giá trị của nó giống như vòng lặp được duyệt hết thì dùng code 2. (Tôi không biết có trường hợp nào đặc biệt hơn không chứ nếu như thế này thì chỉ cần gán i = giaTriCuoi sau vòng lặp là được)
 
Upvote 0
Theo tôi thì:
1. Điểm khác nhau là giá trị của i khi thoát vòng lặp. Với code 1 i sẽ là giá trị khi dk = True, code 2 thì giá trị của i giống như khi vòng lặp được duyệt hết, bằng giaTriCuoi + 1.
2. Nếu muốn sử dụng tiếp biến i và giá trị của nó giống như vòng lặp được duyệt hết thì dùng code 2. (Tôi không biết có trường hợp nào đặc biệt hơn không chứ nếu như thế này thì chỉ cần gán i = giaTriCuoi sau vòng lặp là được)

Đúng như còn thiếu hai chi tiết quan trọng.
Chi tiết thứ nhất liên quan đến cách hoạt động của vòng lặp For trong VBA (nhấn mạnh, VBA. Có nghĩa là ngôn ngữ khác chưa chắc hoạt động như thế)
Chi tiết thứ hai liên quan đến cách sử dụng biến đếm vòng lặp.

Hai chi tiết trên, một cái là lý thuyết ngôn ngữ và cái thứ hai là thực hành.
 
Upvote 0
Chi tiết thứ nhất liên quan đến cách hoạt động của vòng lặp For trong VBA (nhấn mạnh, VBA. Có nghĩa là ngôn ngữ khác chưa chắc hoạt động như thế)
Chi tiết thứ hai liên quan đến cách sử dụng biến đếm vòng lặp.
Về triết lý của vòng FOR thì ở ngôn ngữ nào cũng thế. Nhưng về chi tiết thì có thể có khác biệt trong mỗi ngôn ngữ
Vd. như trong Delphi (Object Pascal):
0. Biến điều khiển i phải có kiểu thứ tự. Tức vd. BYTE, WORD (trong VBA là Integer), Integer (trong VBA là Long). Không thể là Double hay Single được. Nếu thế thì không compile project được.
1. Biến điều khiển i phải là biến cục bộ (local variable). Khai báo biến toàn cục (global) thì vẫn compile project được, chạy nhiều khi vẫn ra kết quả. Nhưng khi compile thì sẽ có cảnh báo (Warning). Tức tôi lưu ý, còn anh làm khác đi thì tôi không đảm bảo luôn luôn không có trục trặc.
2. Trong vòng FOR không được phép thay đổi biến điều khiển i (vd. i := i + 1). Nếu thay đổi i trong vòng FOR thì chả có warning gì cả mà là Error và không thể compile được project.
3. Sau khi ra khỏi vòng FOR một cách tự nhiên thì biến điều khiển không xác định (undefined). Dùng biến i sau FOR trong trường hợp này thì vẫn compile project được, chạy nhiều khi vẫn ra kết quả. Nhưng khi compile thì sẽ có cảnh báo (Warning). Tức tôi lưu ý, còn anh làm khác đi thì tôi không đảm bảo biến i luôn đúng, luôn xác định.
Nhưng nếu ra khỏi vòng FOR bằng code (trong VBA là Exit For, trong Delphi là Break) thì sau FOR biến i có giá trị như lúc ra khỏi For.
Trong VBA sau FOR biến i luôn xác định.
 
Upvote 0
Lý thuyết về vòng lặp For của VBA:

Cũng như mọi ngôn ngữ khác, VBA coi vòng lặp như là một cụm code. Biến điều khiển (biến đếm), biến bước (step), và biến giới hạn (to) có thể coi như là tham nạp vào cụm code. Đến đây thì hết giới hạn "cũng như".
Riêng đối với VBA thì biến đếm là tham byRef, và biến bước, biến giới hạn là mọt hình thức đặc biệt giữa byVal và byRef.
ByRef có nghĩa là hoàn toàn có thể thay đổi, khong cần giới thiệu thêm. Còn hình thức đặc biệt giữa byVal và byRef ra sao mới là chỗ quan trọng:
Khi bạn khai báo câu For bienDem = khoiDau to ketThuc step buoc (nếu không có step thì bước được hiểu ngầm là 1) thì VBA làm các bước TUẦN TỰ sau:
Lưu ý rằng chỉ có bienDem mới bắt buộc là biến, ba cái còn lại có thể là một hằng, 1 biến hay một biểu thức
- Tính giá trị của biểu thức khoiDau, và gán kết quả cho bienDem
- Tính giá trị của biểu thức ketThuc và gán vào 1 biến tạm, kể từ phút này, biến tạm là mọt HẰNG số xác định điểm cuối mà bienDem cần so sánh, ketThuc hết nhiệm vụ của nó. Nếu là một biến thì nó toàn quyền thay đổi mà không hề ảnh hưởng đến hằng kia.
- Tính giá trị của biểu thức buoc và gán vào 1 biến tạm, kể từ phút này, biến tạm là mọt HẰNG số xác định bước tăng của bienDem, tương tự như ketThuc, buoc hết nhiệm vụ của nó
- VBA bắt đầu xét xem có thể tiến vào vòng lặp, tức là bienDem <= trị hằng copied từ ketThuc (nếu trị của buoc là âm thì ngược lại) ở trên

Từ hằng được viết hoa ở trên để nhấn mạnh rằng chúng khong thay đổi nữa. Bên trong vòng lặp, bạn có thay đổi ketThuc hay buoc thì vẫn không ảnh hưởng gì đến vòng lặp. Ví dụ khởi đầu bạn đặt ketThuc là 10, bên trong vòng lặp sửa lại thành 5 thì vòng lặp vẫn chạy đủ đến 10. Tuy nhiên, thay đổi bienDem thì có ảnh hưởng. Tính chất này rất quan trọng để tôi giải thích sự khác nhau của hai đoạn code nhỏ trong câu đố.

Cái khác nhau thứ nhất của 2 đoạn code trên là khi diều kiện thoả; với code 1, vòng lặp CHẮC CHẮN thoát; với code 2, vòng lặp CHƯA CHẮC thoát, vì bên trong vòng lặp, tôi có thể đã sửa ketThuc nhỏ hơn giá trị ban đầu của nó!

Thực hành vòng lặp For của VBA:

Thực hành căn bản thì bạn nào cũng học qua rồi. Khong cần phải nhắc lại. Tôi chỉ nói ra đây mọt vài ứng dụng đặc biệt.

Ứng dụng đặc biệt nhất là khi bạn có sử dụng Exit For.
Hầu hết mọi trường hợp, bạn Exit For khi có một điều kiện nào đó. Vậy thì sau khi thoát khỏi vòng lặp, bạn muốn biét có phải do điều kiện ấy thì sao? Dễ dàng, bạn chỉ viẹc xét lại. Nhưng nếu điều kiện ấy rất rắc rối, là mọt hàm chẳng hạn, thì sao? Dễ dàng, bạn dùng một biến Boolean để chứa trị. Tuy nhiên, cách gọn hơn nữa là xét xem bienDem có vượt quá ketThuc hay không.

Ứng dụng này nói lên cái khác nhau thứ hai của hai đoạn code nhỏ trong câu đố: nếu dùng đoạn code 2 thì không thể dùng biến đếm để thử xem vòng lặp có thoát sớm hay không.

Qua phần thứ nhì của câu đố, tôi hỏi "khi nào dùng code 2"? Câu trả lời là rất hiếm khi. Nhưng tôi đã từng làm rồi (*). Là khi trong code trước đó (cũng trong vòng lặp, nhưng trước phần gán = ketThuc) tôi có một điều kiện khác để Exit For. Và tôi muốn xem lại điều kiện này sau vòng lặp. Thoát bằng Exit For thì luôn luôn biến đếm nằm trong giới hạn. Thoát bằng đièu kiện vòng lặp thì luôn luôn biến đếm vượt ngoài điều kiện ban đầu của vòng lặp (lưu ý, tôi dùng từ ban đầu để phân biệt rằng nếu bên trong vòng lặp bạn thay đổi biến giới hạn thì bạn không thể dùng nó để so sánh được nữa)

(*) code như vậy là trái với luật chuyên nghiệp. Nhưng đôi khi ngộ biến phải tùng quyền.
 
Upvote 0
Em làm thử như anh @huuthang_bd , chọn luôn từ A1 đến A10 vẫn được:
[A1:A10].copy [C1]

Cái này mình copy 1 ô vẫn dũng kiểu đó, công thức gọn hè. Cảm ơn anh nhiều nhé!

Code trên sẽ chép công thức. Ví dụ ô A2 của bạn là công thức A2 = B1 thì tại C2 là công thức C2=D1.
Chú ý là yêu cầu chép value.
 
Upvote 0
Code trên sẽ chép công thức. Ví dụ ô A2 của bạn là công thức A2 = B1 thì tại C2 là công thức C2=D1.
Chú ý là yêu cầu chép value.
Cảm ơn @haonlh, mình thử lại nhé:
code [A1,A2:A10].Copy [C1] : nếu range [A1:A10] tô vàng thì sẽ sao chép vừa giá trị, vừa màu tô vàng.
code [C1:C10] = [A1:A10].Value: chỉ copy giá trị.
 
Upvote 0
Upvote 0
Không phải cái tên của nó đã nói lên tất cả ý nghĩa rồi sao? Hay bạn muốn so sánh nó với ActiveWorkbook?
Tên của nó thì cũng có phần sát nghĩa, nhưng em hỏi là dưới góc nhìn của một VBAer thì nó là gì?
Ps: Nó phức tạp hơn mọi người từng nghĩ về nó.
 
Upvote 0
Trong vba, ThisWorkbook là cái gì?
Mình chả biết nó là cái gì cả trong ví dụ sau:
PHP:
Sub GPE()
 Dim ThisWorkbook As Object, WF As Object

 Set ThisWorkbook = Application.WorksheetFunction
 Set WF = Range("A9:A2")
 MsgBox ThisWorkbook.Sum(WF), , "0"
 MsgBox ThisWorkbook.Sum(Range("A2:A9")), , "1"
End Sub
 
Upvote 0
Mình chả biết nó là cái gì cả trong ví dụ sau:
PHP:
Sub GPE()
 Dim ThisWorkbook As Object, WF As Object

 Set ThisWorkbook = Application.WorksheetFunction
 Set WF = Range("A9:A2")
 MsgBox ThisWorkbook.Sum(WF), , "0"
 MsgBox ThisWorkbook.Sum(Range("A2:A9")), , "1"
End Sub
Đoạn code trên thì tính làm gì, nó là một biến thuộc kiểu object thôi anh. Nếu ta định nghĩa sờ sờ thế kia thì không tính. Ví dụ có mỗi một modul mà ta nhập chẳng hạn.

sub Thu()
msgbox thisworkbook.path
end sub
 
Upvote 0
Hiểu kiểu nông dân là vầy : ThisWorkbook là Cha là Mẹ của cái File Excel đó .............. Nếu ko có Nó là File Excel đó Tèo Téo Teo .............. Xong đơn Giản ko phải nghĩ_)()(-,,,,,,,
 
Upvote 0
Upvote 0
Upvote 0
Mới nghiên cứu cái này thấy hay hay nên mang lên đây đố luôn :D
Câu hỏi: Làm sao lấy được Công thức thực tế áp dụng cho Conditional Formatting của một ô.
 

File đính kèm

Upvote 0
Mã:
Sub LayVung()
    Dim vR
     Set vR = Application.InputBox("Chon vung du lieu", Type:=8)
End Sub

Ở trên là code để người dùng chọn một vùng, rõ ràng phải dùng "Set", vậy đố vui là làm sao vứt cái "set" đó đi mà code vẫn chạy được, lấy được vùng người dùng đã chọn.
 
Upvote 0
Upvote 0
Mã:
Application.InputBox("Chon vung du lieu", Type:=8).Select

Mã:
With Application.InputBox("Chon vung du lieu", Type:=8)
    MsgBox "con cò be bé " & .Address
End With
Đáp án nằm ngoài dự tính, đã lấy được vùng. Ý em là gán vào biến vR, gán vào vR sẽ làm được nhiều thứ hơn, rồi còn vấn đề người dùng nhấn núy cancel nữa
 
Upvote 0
Cho đáp án luôn đi bạn!
Mã:
Sub ChonVung()

    Dim vR
    vR = Application.InputBox("Chon mot vung:", Array("Chon vung"), Type:=8)
   
    If VarType(vR(LBound(vR))) = vbBoolean Then
        MsgBox "Da huy viec lua chon"
    Else
        MsgBox "Da chon vung: " & vR(LBound(vR)).Address
    End If
   
   
End Sub

Đưa đầu vào dưới dạng mảng là được.
Thử câu lệnh sau còn thấy vài điều là lạ:
Mã:
vR = Application.InputBox("Chon mot vung:", Array("Chon vung1", "chon vung2"), Type:=8)
 
Upvote 0
HÃY LẬP DANH SÁCH NHỮNG THÁNG TRONG THẾ KỸ NÀY CÓ 31 NGÀY & NGÀY ĐẦU CỦA THÁNG LÀ THỨ BẢY.​

Bài chỉ giành cho những người dưới 123 bài viết
 
Upvote 0
Theo cách đếm của diễn đàn ta thôi;
Mà ai đã có 123 bài cũng chầu rìa đi nghen!
 
Upvote 0
Hình như tôi mới có 121 bài. Tính luôn bài này là 122.

Nhưng nếu tôi khong hiểu rõ đề bài, hỏi lại bạn thì có bị hết "cô ta" hôn?
 
Upvote 0
Đố vui: Viết code đóng và mở lại chính file chứa code.
 
Upvote 0
Đố ké theo. Giả sử tôi đã có code này, xin cho biết 1 ứng dụng thực tiễn.

(câu đố thực chứ không phải hỏi mắc đâu nhé)
Không biết có ý tưởng nào giống anh không:
- Khi thao tác sai không thể dùng chức năng undo (ví dụ: xóa sheet, chạy macro).
- Một số biến toàn cục được tạo khi file được mở nhưng vì một lý do nào đó nó đã bị giải phóng (thường là do lỗi), lúc này cần phải mở lại file.
 
Upvote 0
Đố vui: Viết code đóng và mở lại chính file chứa code.
Code này trên GPE ........... hên thì trúng
Mã:
Public Sub RestartMe()
    Application.Cursor = xlWait
    Application.OnTime Now + TimeValue("00:00:2"), "OpenMe"
    ThisWorkbook.Close True
End Sub
Public Sub OpenMe()
    Application.Cursor = xlDefault
End Sub
 
Upvote 0
Code này trên GPE ........... hên thì trúng
Mã:
Public Sub RestartMe()
    Application.Cursor = xlWait
    Application.OnTime Now + TimeValue("00:00:2"), "OpenMe"
    ThisWorkbook.Close True
End Sub
Public Sub OpenMe()
    Application.Cursor = xlDefault
End Sub
Tưởng chưa có nên mới đố :D
 
Upvote 0
Đố ké theo. Giả sử tôi đã có code này, xin cho biết 1 ứng dụng thực tiễn.

(câu đố thực chứ không phải hỏi mắc đâu nhé)
Dùng để troll. Ví dụ trong phần OpenMe thiết lập MsgBox "Tắt anh mà dễ à" :D Ai không biết tắt nó trong TaskManager chắc đến nước Reset máy tính.
 
Upvote 0
Dùng để troll. Ví dụ trong phần OpenMe thiết lập MsgBox "Tắt anh mà dễ à" :D Ai không biết tắt nó trong TaskManager chắc đến nước Reset máy tính.
Làm gì có chuyện đó bạn. Khi mở lại lần đầu bạn không mở macro thì làm gì có lần sau :D.
 
Upvote 0

Bài viết mới nhất

Back
Top Bottom