Truy vấn trong SQL?

Chia sẻ bởi:hands
★★★★★
Quảng cáo

Xin chào các bạn.
OT chạy câu lệnh sau:

SELECT
    sc.name + '.' + ta.name TableName,
    SUM(pa.rows) RowCnt
FROM
    sys.tables ta
INNER Join
    sys.partitions pa
ON
    pa.OBJECT_ID = ta.OBJECT_ID
INNER Join
    sys.Schemas sc
ON
    ta.schema_id = sc.schema_id
WHERE
    ta.is_ms_shipped = 0
AND
    pa.index_id IN (1,0)
Group BY
    sc.name,
    ta.name
Order BY
    Sum (pa.Rows)
DESC

Kết quả trả về tên bảng và số dòng của bảng và sắp xếp giảm giần theo cột số dòng trong bảng.

SELECT
    TABLE_NAME,
COLUMN_NAME
FROM
     INFORMATION_SCHEMA.Columns

Kết quả trả về tên bảng và tên các cột trong bảng.

Xin hỏi có câu lệnh nào để trả về 3 dữ liệu: tên bảng, tên cột trong bảng, số dòng trong cột?

"Số dòng trong cột" là như thê nào nhỉ?
thường thì mình nói về số dòng của bảng, vì ô mà không có dữ liệu (NULL) thì vẫn là một ô.

Cái này là tư duy của range, bảng trong excel-vba nên bỏ đi
Khi đã quan niệm là theo Cơ sở dữ liệu – thì đối tượng quan tâm chính nên là Data (dữ liệu) và đối tượng xử lý là table, field, recordset, … và Relationship…

Schema là metadata.
Xáo trộn giữa metadata và data là công việc của data manager hoặc desinger.
Đây là công việc cao cấp. Thường thì sau khi hệ thống phần mềm đưa vào hoạt động cỡ 1 năm (hay 6 tháng, tuỳ theo độ lớn) thì data designer sẽ bắt đầu lập thống kê dữ liệu để hiệu chỉnh lại metadata.
Như đã nói, làm công việc này là hàng cao cấp cho nên ông nội tôi cũng không dám xía vào. Công việc không phải chỉ viết 1 cái sp rồi đọc kết quả. Từ khoá ở đây là "hiệu chỉnh".

Xin chào anh Bình,

Đúng rồi thường là sẽ nói về số dòng của bảng bảng cột nào nhiều dòng nhất thì sẽ lấy số dòng của cột đó.
Ở đây OT muốn lấy dữ liệu của các ô có dữ liệu của các cột trong bảng như minh họa ảnh kèm bên dưới ạ.

Mục đích cũng chỉ là hỏi để xem có câu lệnh này không?
Và OT muốn sử dụng câu lệnh này để kiểm tra thông tin thôi ạ.
Mong anh và mọi người xem giúp ạ.

[ATTACH type="full" alt="Untitled (2).jpg"]2503

Theo như hình của bạn, thông tin vừa lấy từ các table hệ thống vừa truy vấn số dòng NOT NULL trong từng table riêng nên tôi nghĩ là không có cách join các table để lấy thông tin trực tiếp như bạn yêu cầu mà phải dùng cách đi vòng: tạo table tạm lưu thông tin (Tên bảng, Tên Cột, Ô có dữ liệu) rồi mới cập nhật dữ liệu vào table này. Các bạn chuyên về SQL Server có cách khác thì hỗ trợ giùm nhé.

Cách tôi làm như sau: Vì bạn chủ yếu muốn thống kế nên tôi làm kiểu thủ công trực tiếp trong SQL Server nhé.

1. Tạo table tạm:

USE NORTHWIND --Đổi tên Database của bạn.
GO
CREATE TABLE myDBInfo (
    TblName VARCHAR(30),
    ColName VARCHAR(30),
    NumRow INT
);

2. Insert dữ liệu từ INFORMATION_SCHEMA.COLUMNS vào table tạm:

USE NORTHWIND
GO
INSERT INTO myDBInfo SELECT TABLE_NAME, COLUMN_NAME,0 FROM INFORMATION_SCHEMA.COLUMNS

3. Tạo các stored proc. (SP) để thực thi việc cập nhật dữ liệu: tạo 2 SP

– SP: UpdatemyDBInfoTbl

USE [NORTHWIND]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[UpdatemyDBInfoTbl]
@TName nvarchar(30),
@CName nvarchar(30)
AS
BEGIN
SET NOCOUNT ON;
Declare @sql nvarchar(300);
    Set @sql='UPDATE myDBInfo SET NumRow = (SELECT count(*) FROM ' +  @TName + ' WHERE ' + @CName + ' Is Not Null) WHERE myDBInfo.TblName =''' + @TName + ''' AND myDBInfo.ColName =''' + @CName + ''''
    EXEC sp_executesql @sql
    --print @sql
END

– SP: GetNumRowNotNull

USE [NORTHWIND]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[GetNumRowNotNull]

AS
BEGIN
declare @TName varchar(30)
Declare @CName varchar(30)
declare cur CURSOR LOCAL for
    select TblName, ColName from myDBInfo

open cur

fetch next from cur into @TName, @CName

while @@FETCH_STATUS = 0 BEGIN
--print @TName
--print  @CName
    --Chạy Store Proc. cho từng dòng

exec dbo.UpdatemyDBInfoTbl @TName, @CName

fetch next from cur into @TName, @CName

END

close cur
deallocate cur
END

==> Xong. Bây giờ chạy lệnh thực thi SP trên.

Exec sp_executesql GetNumRowNotNull

Và xem kết quả:

select * from mydbinfo

2504

– Trong Excel bạn dùng ADO Command để gửi lệnh yêu cầu SQL Server thực thi cái stored proc. "GetNumRowNotNull".
– Sau đó bạn dùng ADO Recordset để lấy dữ liệu từ table "myDbInfo" về Excel sheet như cách bạn đang làm.
– Một cái quan trọng để tránh lỗi cho cái SP trong SQL server là đặt tên Table, tên Field không có khoảng trắng nhé.

www.giaiphapexcel.com/diendan/threads/truy-v%E1%BA%A5n-trong-sql.143453/

Thiết kế Tổng đãi ngộ (Total Rewards) theo khung SHRM
Khóa học SprinGO phù hợp

Thiết kế Tổng đãi ngộ (Total Rewards) theo khung SHRM

Khóa học “Thiết kế Tổng phần thưởng (Total Reward) chuẩn khung SHRM” giúp bạn nắm vững toàn bộ hệ thống đãi ngộ theo chuẩn...

Xem khóa học
★★★★★ 5 ★ 1 👤 30 ▥ 0
Quảng cáo

Bạn nên đọc

30 Responses

  1. hands says:

    Xin chào ongke0711,
    Cảm ơn anh rất nhiều OT đã hiểu cách làm ạ.
    Như vậy sau lần đầu (bảng "myDbInfo" đã được thiết lập) thì mỗi lần kiểm tra phải xóa cái bảng "myDbInfo" đi và lặp lại các thao tác trên phải không ạ?
    Nếu vậy có câu lệnh nào cập nhật lại thông tin vào bảng "myDbInfo"không? theo OT hiểu các lần thứ 2 trở đi chỉ cần thao tác từ bước:

    – SP: GetNumRowNotNull
    ….

    Là được phải không ạ?
    Nếu không phải như vậy thì phiền anh có thể viết thêm câu lệnh xóa bảng "myDbInfo" với điều kiện nếu nó đang tồn tại trong Database ạ.
    OT hiện giờ chưa có điều kiện môi trường để test nên xác nhận ạ 😀

    Em chạy code sau để cập nhật lại cái SP "GetNumRowNotNull".
    Sau này chỉ cần chạy cái SP trên, nó sẽ xoá nội dung table myDBInfo (không xoá table), cập nhật lại tên table, column sau đó cập nhật số dòng.

    USE [NORTHWIND]
    GO
    /****** Object:  StoredProcedure [dbo].[GetNumRowNotNull]    Script Date: 07/08/2019 7:25:45 PM ******/
    SET ANSI_NULLS ON
    GO
    SET QUOTED_IDENTIFIER ON
    GO
    ALTER PROCEDURE [dbo].[GetNumRowNotNull]
    
    AS
    BEGIN
    
    DELETE FROM myDBInfo
    INSERT INTO myDBInfo SELECT TABLE_NAME, COLUMN_NAME, 0 FROM INFORMATION_SCHEMA.COLUMNS
    
    declare @TName varchar(30)
    Declare @CName varchar(30)
    declare cur CURSOR LOCAL for
        select TblName, ColName from myDBInfo
    
    open cur
    
    fetch next from cur into @TName, @CName
    
    while @@FETCH_STATUS = 0 BEGIN
    --print @TName
    --print  @CName
        --Chạy Store Proc. cho từng dòng
    
    exec dbo.UpdatemyDBInfoTbl @TName, @CName
    
    fetch next from cur into @TName, @CName
    
    END
    
    close cur
    deallocate cur
    END
  2. hands says:

    Xin chào các bạn,
    Trong SQL-DB , Oanh Thơ(OT) đang sử dụng câu lệnh sau để lấy dữ liệu từ bảng SODER:

    select CODE,CDATE,QTY from SODER

    Kết quả dữ liệu xuất ra như Sheet1.
    Giờ OT mong muốn lấy dữ liệu như dữ liệu trong sheet2 với điều kiện >="01/09/2019" và < "01/10/2019"
    Thì câu lệnh truy vấn sẽ như thế nào ạ?

    Bạn thử
    select CODE,CDATE,QTY from SODER where cdate >='01/09/2019' and cdate < '01/10/2019'

    Cảm ơn Nguyễn Hồng Quang đã giúp đỡ
    OT đã thử như vậy nhưng cdate không phải là dữ liệu ngày tháng năm ạ.
    Dữ liệu như OT gửi trong file kèm đó ạ

    – Dùng hàm DateSerial() nhé bạn.
    – Không được dùng các từ khoá của hệ thống làm tên Fieldm tên biến. "CDate"

    SELECT Code, Ngay, Qty, DateSerial(Left(,4),Mid(,5,2),Right(,2)) As NgayChuyen FROM SORDER WHERE Between #" & txtTuNgay & "# AND #" & txtDenNgay & "#"

    Xin chào ongke0711
    Cảm ơn anh đã quan tâm và giúp đỡ.
    OT truy vấn câu lệnh sau:

    SELECT Code, CDATE, Qty, DateSerial(Left([CDATE],4),Mid([CDATE],5,2),Right([CDATE],2)) As NgayChuyen FROM SORDER  WHERE [NgayChuyen] between '2019-09-01' and '2019-10-01'

    Thì báo lỗi sau:
    Msg 195, Level 15, State 10, Line 1
    'Mid' is not a recognized built-in function name.

    Nhờ anh xem giúp ạ.
    Bảng SORDER này cột của nó đặt là "CDATE" ạ

    Oh. Em xài SQL Server 2008 à? Em thay thế hàm Mid bằng hàm SubString đi.

    Dạ OT sử dụng SQL-2017 anh ạ, lại phát sinh lỗi này anh ạ:

    Msg 195, Level 15, State 10, Line 1
    'DateSerial' is not a recognized built-in function name.

    muốn truy vấn chuẩn thì đầu tiên phải biết datatype của các cột là gì nhé.

    t-sql thì: select … cast( as date) as xx where fieldate between '2019-07-01' and '2019-07-31'
    T-SQL:
    CDATE Like '201907%'
    CDATE Like '201907__'

  3. hands says:

    Dạ OT sử dụng SQL-2017 anh ạ, lại phát sinh lỗi này anh ạ:

    Msg 195, Level 15, State 10, Line 1
    'DateSerial' is not a recognized built-in function name.

    🙂 Lỗi do anh lẫn lộn giữa hàm bên VBA và SQL Server, râu ông nọ cắm cằm bà kia.
    Em phải đổi NVarchar thành DateTime thì mới thực hiện các phép so sánh + – ngày tháng chính xác.
    Dùng hàm DateFromParts() tương tự hàm DateSerial() trong VBA. Ngắn gọn là dùng CAST hoặc CONVERT.

    Convert(nvarchar, CDATE, 23)

    SELECT CODE, CDATE, QTY FROM SORDER WHERE Convert(nvarchar,CDATE,23) between '2019-09-01' And '2019-10-01'

    A! Được rồi ạ, OT xin trân trọng cảm ơn sự quan tâm giúp đỡ của mọi người ạ.
    CDATE chắc là định dạng Date nên OT thử câu lệnh:

    ..WHERE CDATE between '2019-09-01' and '2019-10-01'

    OK rồi ạ

    kiểm tra mới chắc chắn đc nhé, vì trong nhiều trường sẽ xảy ra trường hợp tự ép kiểu nhé. mình dùng điện thoại nên không xem cụ thể thế nào nhưng qua phỏng đoán code trên thì cột của bạn là yyyymmdd và nó là varchar chứ không là date, date trong ms sql là yyyy-mm-dd, trong th của bạn là ép kiểu rồi.

    bạn chạy câu lệnh là biết date trong ms sql fortmat chuẩn ntn: select getdate()

    Nếu CDATE là kiểu date thì phải bảo đảm nó là Short Date, không phải DateTime.
    Bởi vì >= '20190701' And < '20190801'
    khác với Between '20190701' and '20190731'
    Trường hợp 1 sẽ kể luôn những '20190731 23:59:59 99.99999'. Trường hợp 2 cứ 1 giây là loại rồi.

  4. hands says:

    Xin cảm ơn mọi người đã giúp đỡ,
    OT kiểm tra trong CSDL thì nó như thế này ạ, không biết câu lệnh sẽ xử lý theo cách nào là chuẩn ạ.
    2506

    Nhưng dù sao nhờ có sự giúp đỡ của mọi người nên vấn đề của OT cũng đã được giải quyết ạ.

    bạn sử dụng hàm cast thôi cast(cdate as date) between x and y. còn k bạn k cần cast thì nó tự ép kiểu cho bạn

    Kiếm cái thằng thiết kế CSDL mà hỏi nó số 1 ở sau có nghĩa là gì.
    Điển hình đối với một số phần mềm kế toán, người ta thêm mọt ký hiệu sau ngày tháng để chỉ quý. Đối với một số nước trên thế giới tháng 7+8+9 là quý 1.

    Troing hiện tại thì với yêu câù của bạn, cứ lấy 6 ký tự đầu là tháng, 8 ký tự là ngày.
    Dạng YYYYMMDD đã được chuẩn để có thể so sánh theo kiểu chuỗi, không cần phải cast. Chỉ khi nào cần cộng trừ ngày tháng mới phải cast.

  5. hands says:

    Xin chào mọi người,
    Nhờ mọi người giúp đỡ OT câu lệnh Select để lấy dữ liệu từ 4 bảng sau đó tạo ra vùng dữ liệu như bảng bên dưới với ạ.

    2508

    Trường [Ngày tháng] không có sao bạn OT?
    Nếu dữ liệu tồn kho, đơn hàng mấy năm thì máy chạy mệt à.

    Xin chào Anh ongke0711
    Dạ tất nhiên là có Anh ạ, OT chỉ ví dụ vậy để biết cách vận dụng ạ 😀
    Nhờ anh xem giúp ạ.

    Tôi hỏi kỹ để tránh việc đưa một câu lênh T-SQL mà kết quả của nó không có ý nghĩa gì hết, rồi em áp dụng cũng không được.
    Cái mấu chốt nằm ở cái table STOCK của em. Nó là tồn kho ngay tại thời điểm chạy query hay tồn đầu kỳ (từng tháng).
    – Nếu là tồn đầu kỳ thì đưa vào truy vấn số lượng bán (tôi suy ra theo cái tên SOrder – Sales Order) tồn kho không chính xác vì còn phải + thêm nhập TK tính tới ngày xuất bán -> lúc đó mới có ý nghĩa: đối chiếu với tồn kho thời điểm (Quantity On Hand) trước khi xuất bán.
    – Nếu đó là table tồn kho thời điểm xét thì không có gì để nói. 🙂
    – Có trường hợp "MH001" bán cho các khách hàng khác không? vd KH002, KH005… Khi nhìn table KHACHHANG tôi lại nghĩ nó giống Nhà Cung Cấp và cái SORDER trở thành table Mua hàng (PURCHASE ORDER) hơn. Khi đó lại sẽ phát sinh các vấn đề khác về tổ chức lưu thông tin (hoặc do em cung cấp thông tin table liên quan quá ít nên không nhìn thấy tổ chức tổng thể của CSDL).
    Nếu cái bảng kết quả em muốn là tồn kho và số lượng đặt hàng của từng nhà cung cấp thì table STOCK thiếu thông tin Mã NCC của từng Mã hàng tồn kho.
    Nếu nói mỗi Mã Hàng chỉ có 1 NCC cố định nên gán Mã Hang đi theo NCC rồi như table KHACHANG, truy vấn từ Mã hàng sẽ ra mã NCC thì cũng sai vì chỉ nhìn ngắn hạn. Sau này có đổi mua của NCC khác thì theo dõi như thế nào? tồn kho NCC cũng, tồn kho NCC mới…

    Tại tôi muốn hiểu rõ chút về quan hệ giữa các Table, các field như thế nào và thông tin từ em cung cấp cũng không nhiều nên hỏi tùm lum là vậy rồi mới ra được câu lệnh truy vấn. Chứ để ra kết quả như trên thì không có gì phức tạp.

    Xin chào ongke0711,
    Cảm ơn anh đã quan tâm & giúp đỡ ạ.
    Các vấn đề anh hỏi liên quan đến tính toán OT chưa nghĩ đến, có lẽ nó thuộc phần nâng cao hơn một chút ạ, các vấn đề này OT sẽ tạo thêm ví dụ khác ạ , lúc đó có gì anh lại chỉ dẫn thêm ạ.
    Còn thực sự OT đang tìm hiểu về câu lệnh select Join từ nhiều bảng (tương tự hàm tìm kiếm trong Excel) thôi ạ nên đưa ví dụ minh họa đơn giản như vậy.
    Cảm ơn anh nhiều ạ.

    Nếu chỉ đơn giản là tham khảo thì em thử câu lệnh này:

    – Theo như hình của em, anh lấy trường từ table DSSANPHAM.

    SELECT DSSANPHAM.MASP, DSSANPHAM.TENSP, KHACHHANG.MKH, STOCK.SOLUONG2, SORDER.SOLUONG1
    FROM (KHACHHANG RIGHT JOIN (DSSANPHAM LEFT JOIN SORDER ON DSSANPHAM.MASP = SORDER.MSP) ON KHACHHANG.MASP = DSSANPHAM.MASP) LEFT JOIN STOCK ON DSSANPHAM.MASP = STOCK.MSP

    – Nếu để đối chiếu tồn kho với table xuất hàng (SORDER) thì trường mã SP phải lấy từ table SORDER, khi đó câu lệnh phải đổi lại là:

    SELECT SORDER.MSP, DSSANPHAM.TENSP, KHACHHANG.MKH, STOCK.SOLUONG2, SORDER.SOLUONG1
    FROM KHACHHANG INNER JOIN (DSSANPHAM INNER JOIN (SORDER LEFT JOIN STOCK ON SORDER.MSP = STOCK.MSP) ON DSSANPHAM.MASP = SORDER.MSP) ON KHACHHANG.MASP = SORDER.MSP

    Quản lý hàng hoá (Inventory Control) là công việc lớn. Hệ thống quản lý nếu có đầy đủ thông tin xuất nhập tồn thì rất phức tạp. Và vì mõi cơ quan có quy trình làm việc khác nhau cho nên rất khó thiết lập.
    Để thiết lập csdl, người ta có hai chọn lựa:
    1. chọn một hệ thống có sẵn, và tương đối gần với quy trình của mình. Và chỉnh lại chính quy trình của mình cho phù hợp.
    2. mướn chuyên viên theo dõi quy trình của mình và thiết kế mọt hệ thống thật phù hợp.

    Đấy là tôi nói chỉ Inventory Control. Nếu phải theo dõi đơn đặt hàng (Purchase Orders/Sales Orders) thì còn lớn hơn nhiều.

    Theo như cái bảng yêu cầu ở bài #31 thì bạn cần xem stock có đủ để cung cấp cho đơn đặt hàng hay không. Công việc này có thể thuộc về khâu quản kho nhưng cũng có thể thuộc về khâu bán hàng. Tôi doán ở đây bạn muốn nói khâu kho – nếu khâu bán hàng thì phải thêm phần ngày giao hàng và phân bổ ưu tiên khách hàng/đơn hàng khi không đủ hàng, rất rắc rối.

    Nói về lý thuyết 1 chút. Theo lý thuyết CSDL LH thì bạn chỉ cần 1 bảng phát sinh nhập xuất hàng. Tồn kho ở mọi thời điểm sẽ là tổng các phát sinh kể từ ngày đầu tiên cho đến thời điểm ấy. Trên thực tế, người quản lý CSDL ai cũng biết rằng dạng CSDL LH là một con khủng long. Tình trạng lý tưởng là tối thiểu chuẩn bậc 3 (3rd order normalisation) lắm khi ảnh hưởng đến tốc độ truy vấn.

    Vì vậy, những CSDL có liên quan đến khái niệm tồn đầu/tồn cuối thường được thiết kế theo dạng phân từng phần (Partitioned). Điển hình như csdl của bạn, ngừoi ta có thể thiết kế bảng Stock theo năm. Tồn đầu kỳ có nghĩa là tồn đầu năm. Và tồn tại thời điểm có nghĩa là tồn đầu năm cộng/trừ các phát sinh trong năm.

    Tuy nhiên, nhìn theo hình ở bài #31 thì có lẽ bảng của bạng theo dạng tồn tại chỗ. Mỗi phát sinh sẽ cộng/trừ thẳng vào trường "tồn" trong bảng. Cái báo cáo mà bạn muốn trong bảng thứ 5 là loại báo cáo dữ liệu cấp thời (impromptu). Nói cách khác, nó chỉ có giá trị đúng vào thời điểm truy vấn, và nó giả sử rằng mọi đơn đặt hàng bán và mua đều nằm trong khoảng thời gian tính toán – cái giả sử này là điều mà tác giả bài #34 muón bạn phải khẳng định.

    Tóm lại, nếu:
    1. bảng 5 trong hình là báo cáo xem tình trạng tồn kho có đủ cung ứng đơn đặt hàng, VÀ
    2. trường SoLuong2 chứa tồn kho đến thời điểm hiện tại, VÀ
    3. trường SoLuong1 chứa số lượng nằm trong đơn đặt hàng (chưa giao)
    Hội đủ 3 điều kiện trên thì câu truy vấn khá đơn giản.

  6. hands says:

    Dạ OT xin cảm ơn Bác VetMini và Anh ongke0711 đã góp ý và chỉ dẫn ạ.

    Liên quan đến truy vấn dạng dữ liệu kiểu nhập,xuất, tồn từ SQL… OT sẽ gửi dữ liệu cụ thể nên sau.. đây cũng là một vấn đề OT đang rất quan tâm mà chưa giải quyết và đề cập đến được ạ.
    Rất mong khi đó được Bác và Anh cùng mọi người giúp đỡ ạ.

    Tôi có nói rõ là nó rất rắc rối. Không thể tư vấn theo kiểu diễn đàn được. Bắt buộc phải ăn dầm nằm dề tại kho mà quan sát từng công việc.
    Việc cốt kiếc này nọ là chuyện tay trái của tôi cho nên tôi có thể sẵn sàng làm.
    Nhưng thiết kế CSDL cho Inventory Control thì tôi không sẵn sàng. Không phải tôi giấu nghề, nhưng tư vấn mà không biết rõ quy trình và không chủ động được quy trình thì tôi từ chối.
    Tôi không hề mắc cở khi có ngừoi nói "thằng VM viết code rác rưởi này à?".
    Nhưng tôi không chấp nhận một ngày nào đó, có người nói "thằng VM thiết kế cái CSDL rác rưởi này à?"

    (*) chủ động quy trình có nghĩa là nếu tôi nói "làm như thế này mới đúng" thì cơ quan phải làm như thế; không có chuyện "nhưng mà…"

    Con cảm ơn Bác VetMini nhiều ạ.
    Thực ra CSDL, khi cài đặt phần mềm đã có tại Server rồi Bác ạ. Nên khâu thiết kế là không cần nữa Bác ạ.
    Nhưng phần mềm mới chỉ quản lý nhật ký xuất, nhập hàng ngày và tồn theo tại thời điểm hiện tại, tồn kho đầu kỳ. Không thể lấy được tồn kho theo từng thời điểm.

    Ví dụ hiện đang là thời điểm ngày 5/8 trong hệ thống csdl chỉ quản lý tồn kho đầu kỳ sau kiểm kê hết ngày 31/7 và tồn ở thời điểm hiện tại là ở ngày 5/8 là số lượng cụ thể bao nhiêu.

    Nhưng khi đứng ở ngày 5/8 nếu con muốn biết dữ liệu tồn kho từ 1/7 đến ngày 25/7 của mã hàng A là bao nhiêu thì hệ thống không có.
    Do đó con muốn sử dụng câu truy vấn trong sql để làm việc này.
    Về dữ liệu cụ thể, con sẽ cung cấp sau ạ, vì hiện con cũng đang chưa hiểu dữ liệu ở các bảng trong sql liên quan như thêa nào để gửi lên ạ.
    OT cảm ơn Bác VetMini và Anh ongke0711 đã quan tâm và giúp đỡ ạ.

    Nếu CSDL đã có kết chuyển tồn cuối kỳ (hoặc tồn đầu) theo tháng và tồn tại thời điểm rồi thì chắc chắn là đã có giải thuật tính tồn kho theo khoảng thời gian cần truy vấn rồi. Chắc là bạn chưa biết nó nằm trong cái stored proc. nào để gọi ra thôi.

    Tôi khuyên bạn nên tìm tài liệu của người thiết kế hệ thống QL hàng mà đọc.
    Một phần mềm cài đặt luôn luôn có hồ sơ và tài liệu về thiết kế (design documents, user's guide,…). Nếu không có thì cơ quan bạn mướn nhằm thằng cà chớn cài đặt rồi.
    Hệ thống tôi thiết kế ngoài những tài liệu kể trên còn cả tài liệu thẩm định về hiệu quả sau 3, 6, và 12 tháng.
    Hầu hết những stored procedures liên quan đến cập nhật dữ liệu cũng đều có hồ sơ ghi rõ chức năng.

    Chỉ riêng cái câu tôi tô đỏ ở trên cũng đủ cho thấy bạn không hiểu nhiệm vụ của QL hàng tồn kho rồi.
    Hàng tồn kho là con số cho biết tình trạng của hàng ở một thời điểm (ngay tại ngày), không thể là con số ở một khoảng thời gian (từ ngày đến ngày). Trong khoảng thời gian thì bạn chỉ có thể lấy những con số thống kê, ví dụ từ ngày xxx đến ngày yyy thì trung bình tồn kho là 123, cao nhất là 456, thấp nhất là 0, có 3 lần thiếu hàng, vân vân…
    Và cả nhu cầu lẫn khả năng cung cấp con số thống kê đã được thẩm định từ lúc thiết kế.

    Xin chào Bác VetMini,
    Đúng rồi ạ, hiện con đang chưa hiểu được về nhiệm vụ quản lý hàng tồn kho.
    Còn về tài liệu cho phần mềm của công ty con gần như không có thông tin gì ạ. Trong giao diện phần mềm thì không thể lấy dữ liệu xuất nhập tồn theo từng thời điểm trong quá khứ được, trong khi đó kế toán thi thoảng vẫn cần báo báo để phụ vụ thanh tra thuê ạ.

    Chắc là ý em OT muốn lấy báo cáo Nhập – Xuất – Tồn trong một khoảng thời gian nhưng không diễn giải đúng đó anh VetMini. Tồn kho ở từng thời điểm nhâp xuất.

    Xin chào Anh ongke0711,
    Đúng đúng rồi Anh ạ, OT cần báo cáo này. Nhưng do phần mềm không có chức năng tạo báo cáo xuất nhập tồn trong khoảng thời điểm bất kỳ trong quá khứ nên OT mới cần truy vấn trong SQL ạ.

    Không biết câu lệnh truy vấn dựa vào nhật,ký xuất nhập ở các thời điểm liệu có tính ra được tồn kho được hay không ạ?
    Nhưng trước hết OT cần tìm hiểu thêm về các bảng dữ liệu trong CSLD có những gì đã ạ. Rồi nhờ Bác/Anh và mọi người xem giúp ạ.
    Híc,anh IT cho động vào máy chủ mà OT thấy ghê quá ạ,nghịch linh sợ nó hư dữ liệu thì mệt lắm ạ.
    Nên OT chỉ dám sử dụng câu lệnh Select trong SQL thôi ạ.

    Cảm ơn Bác và Anh đã góp ý.

  7. hands says:

    Pm mua hay đặt hàng viết riêng vậy bạn OverTime ? Của VN hay nước ngoài?
    Nếu của VN thì hầu như đặt hàng họ sửa chữa, thêm bớt được. Cứ liên hệ họ và dắt Bác theo. Nhớ phải có thời gian, điều kiện bảo hành rõ ràng nhen, không thì lại tốn Bác đấy
    Còn không được thì dắt Bác qua cho Ông Kẹ. Mấy vụ database này trên GPE nó sợ bạn Ông Kẹ lắm đấy 🙂

    Có những ngôn từ hơi nóng về chính trị một chút.
    Người "anh hùng" có thể ỷ mình bất khả xâm phạm nhưng kẻ "trượng phu" phải biết suy nghĩ cho hoàn cảnh của người khác.

    Bác CuAnh nói quá rồi… CSDL tôi còn chưa sạch nước cản nữa mà. Chẳng qua là có xem qua vài cái CSDL về bán hàng, nhân sự nên góp vài ý kiến thôi. 🙂

    Tính tồn kho thời điểm đã qua thì không khó lắm.
    Nếu là SQL Server (chính bản, không phải Express) thì người thiết kế thường có cái view cho biết tồn kho ở từng đầu kỳ. Ví dụ tôi ở tài khoá 2019 thì tôi sẽ có 12 cái views của 12 tháng 2018, và 8 cái views cho tháng 1-8 2019.
    Việc tính tồn kho từng thời điểm chỉ là truy vấn cái view đúng đầu kỳ và cộng/trừ đi những phát sinh cho đến thời điểm cần tính.
    Nếu không có những cái views này thì:
    1. Bảo người thiết kế viết sp để thiết lập chúng, HOẶC
    2. Chịu khó mò cái tồn kho gần nhất. Và làm tương tự như trên

    Tính tồn kho dự toán tương lai thì mới khó. Vì phải tính tình trạng từng hợp đồng đã ký, hoặc dự định ký nhưng chưa xuất kho.
    (có một số hệ thống xuất kho rồi vẫn tính là hàng còn trong tay, bên kia nhận rồi mới tính là hàng không còn của mình)

    Có nhật ký Xuất – Nhập + Tồn kho đầu kỳ (hoặc cuối kỳ tuỳ theo CSDL của em) là truy xuất ra được tồn kho luỹ tiến theo từng nghiệp vụ Xuất Nhập. Đừng kêu tính đơn giá bình quân gia quyền hay báo cáo NXT theo FIFO, LIFO là được…hehe.. 🙂

    Cảm ơn Bác VetMini , Anh ongke0711 đã luôn quan tâm & giúp đỡ OT ạ.
    OT cũng chưa nghiên cứu được dữ liệu trong CSDL ở các bảng liên quan với nhau như thế nào nên chưa đưa dữ liệu lên được.
    Bác và Anh có tài liệu hoặc biết link nào tiếng Việt về CSDL/ liên quan cụ thể đến quản lý kho không ạ gửi cho OT thử "nghịch" với được không ạ.
    Híc anh IT cấm không cho OT sử dụng câu lệnh nào ngoài câu lệnh SELECT trong DB thật ạ, còn muốn làm gì thì anh ý cũng tạo ra một DB bản sao cho nghịch mà OT cũng chưa biết nên bắt đầu từ đâu ạ.
    Khổ có trường, có nơi thực hành nhưng không có Thầy ạ T_T

    Bắt đầu từ đâu thì không biết máy ở nhà của bạn đã cài đặt SQL server chưa? Bạn nói anh IT đó sao lưu cho bạn một cái DB riêng, sau đó chạy riêng trên máy bạn và test, cái này mình hay làm cho công ty mình, do công ty mình chỉ cho phép truy cập vào database qua các máy trên cty, nên mình thường hay sao lưu 1 bản rồi về nhà chạy để test.

  8. hands says:

    Xin chào các bạn.
    Nhờ các bạn giúp đỡ OT trường hợp sau với ạ.

    Trong màn hình giao diện phần mềm quản lý kho sử dụng câu lệnh:

    select cast(0 as decimal(29,3))  as TONKHO 
    ...

    Cột TONKHO có số liệu tồn kho.
    Nhưng trong màn hình SQL.Dabase OT chạy câu lệnh đó vào thì kết quả là 0.

    Xin hỏi phải sửa trong SQL.Dabase như thế nào để có dữ liệu giống như giao diện phần mềm ạ

    Cách tốt nhất hỏi nhà sx phần mềm (hoặc đọc được code của họ), vì ai biết sau khi nhập chuỗi Sql đó vào trong chương trình xử lý gì tiếp (chẳng hạn thay số 0 như suy đoan của ongke trên , hoặc là khác thì sao)

    Thay số 0 thành tên field tưng ứng cần chuyển đổi.
    Cast (Tên field As Decimal…..)

    Cảm ơn ongke0711 đã giúp đỡ
    Anh có thể chỉ cho OT dựa vào đâu để biết Tên field này không ạ?

    Đúng như bạn ppc0312 đã đề cập, thường câu lệnh này chỉ là trung gian trong một truy vấn gì đó như Union query, stored proc… chứ câu lệnh này không phải để em thay tên field. Anh gợi ý cách thay tên field để em hiểu về cú pháp và để truy vấn ra số tồn kho.
    Muốn hiểu hơn thì phải có toàn bộ đoạn code trên mới phán đoán được.

    Xin chào ongke0711,
    Cảm ơn Anh đã giúp đỡ, OT đã xử lý được vấn đề bằng cách sử dụng vba để tính toán sau khi lấy được các dữ liệu liên quan.
    ——–
    Một vấn đề khác nhờ Anh và mọi người xem giúp ạ, trong câu lệnh sau:

    ....
    strSQL = "SELECT * FROM tblODER WHERE MAHANG LIKE 'KH%'"
    rs.Open strSQL, conn
    Dim ws As Worksheet
    Set ws = Application.Sheets.Add
    ws.Range("A1").CopyFromRecordset rs
    Msgbox rs.Fields.Count
    ....

    rs.Fields.Count sẽ trả về số cột của rs
    Vậy làm thế nào để kiểm tra được số dòng trả về của rs (không dùng phương pháp kiểm tra trên bảng tính excel sau khi ghi xuống bảng tính)

    sử dụng
    Recordset.RecordCount

  9. hands says:

    Cảm ơn bạn, OT cũng đã tìm hiểu và sử dụng câu lệnh trên (rs.Recordcount )
    kết quả trả về – 1, trong khi dữ liệu trả về rất nhiều dòng. Phiền Bạn có thể hướng dẫn chi tiết hơn được không ạ?

    sử dụng rowcount và phần chay truy vấn với tham số

    rs.Open strSQL, conn,3,1

    Mình k nhớ có chính xác k.

    Cảm ơn
    Sử dụng quanluu1989, phiền bạn có thể hướng dẫn chi tiết hơn được không?

    RecordCount trong ADO Recordset chỉ chạy được với 3 loại "CursorType" là: adOpenKeySet (1), adOpenDynamic (2), adOpenStatic (3)
    CursorType: adOpenForwardOnly(0) nó không đọc được nên trả về giá trị -1, tức là cho biết có record tồn tại thôi.
    Nếu chuỗi kết nối không khai báo thì ADO sẽ sử dụng CursorType mặc định là: adOpenForwardOnly – 0

    Hic, cụ thể là thế nào vậy Anh?
    OT muốn kiểm tra số dòng dữ liệu lấy từ DB vào Excel, trong trường hợp nếu dữ liệu đến 100,000 dòng thì báo lỗi dữ liệu quá nhiều trước khi đưa xuống bảng tính excel.

    🙂 Em dùng câu lênh như của quanluu1989 đó. Anh chỉ giải thích thêm là phải khai báo Recordset theo kiểu CursorType đó thì RecordCount mới trả về kết quả đúng.

    Cách 1: dùng khai báo sớm nhé. (Early Binding)

    [FONT=courier new]Dim oRS As ADODB.Recordset
    Set oRS = New ADODB.Recordset
            With oRS
                .[COLOR=rgb(41, 105, 176)]CursorType = adOpenKeyset[/COLOR]
                .CursorLocation = adUseClient
                .LockType = adLockBatchOptimistic
                .Open strSQL, oCnn
                [COLOR=rgb(41, 105, 176)]Msgbox "So dong: " & oRS.RecordCount[/COLOR]
             End With[/FONT]

    Cách 2: như của quanluu1989

    [FONT=courier new]   Dim oRS As ADODB.Recordset
       Set oRS = New ADODB.Recordset
       oRS.Open strSQL, oCnn, adOpenKeyset, adLockBatchOptimistic
       Msgbox "So dong: " & oRS.RecordCount[/FONT]

    Cảm ơn quanluu1989, cảm ơn Anh ongke0711
    OT đã thử 2 cách của Anh ongke0711, cả 2 đều OK rồi ạ.

  10. hands says:

    Nhờ các A/c xem giúp e lỗi này với ạ. Thank

    Lỗi này do cái ConnectString của bạn bị sai nên không open được
    Xem lại cái Chr(39) trong chuỗi Connect. Nếu nhớ không nhầm thì Chr(39) là dấu nháy đơn (') thì phải

    Cảm ơn a đã trợ giúp. e khắc phục được vấn đề của e rồi ạ.
    Vấn đề của e là lỗi format định dạng ngày tháng. do lúc trước e để định dạng ngày tháng để so sánh dạng số (long), dữ liệu trong bảng dạng date nên khi so sánh bị ép kiểu dẫn đến chỉ làm được 1 số dòng sau đó báo lỗi runtime .. như trên.
    Cách khắc phục của e là chuyển cái ngày tháng so sánh thành 1 string có định dạng theo cú pháp "mm-dd-yyyy". sau khi chuyển định dạng như vậy thì chạy ok rồi ạ.

  11. hands says:

    Xin chào các bạn.
    Nhờ các bạn giúp tôi một câu lệnh truy vấn lấy ngày gần nhất so với thời điểm hiện tại trong database với ạ.
    Cụ thể trong file đính kèm Sheet"Data" là bảng dữ liệu trong CSDL tôi cần truy vấn đến, và sheet KQ là dữ liệu mong muốn tôi cần lấy được từ bảng Data ạ.

    @OT thử, điều kiện là các ngày đều trong quá khứ và hiện tại

    Option Explicit
    Sub a()
        Dim cnn As New ADODB.Connection
        Dim rs As New ADODB.Recordset
        Dim s As String
        s = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & ThisWorkbook.FullName & " ;Extended Properties=""Excel 12.0 Xml;HDR=YES"";"
        cnn.Open s
        s = "SELECT * FROM [DATA$] WHERE DATE = (SELECT MAX(DATE) FROM [DATA$])"
        rs.Open s, cnn
        Sheets("KQ").Range("A2").CopyFromRecordset rs
        rs.Close
        Set rs = Nothing
        cnn.Close
        Set cnn = Nothing
    End Sub

    Trường hợp này OT hỏi về câu truy vấn trực tiếp trong SQL_Server ạ chứ không phải lấy từ Sheet DATA sang sheet KQ đâu ạ.
    Tuy nhiên OT cũng đang cần đoạn code để lọc dạng này từ Sheet này sang Sheet khác, cảm ơn bạn đã cho OT một cách để tham khảo.
    Vậy là câu lệnh OT cần để thực hiện trực tiếp trên SQL_Server là: "SELECT MAX(DATE) FROM DATA".
    Híc câu lệnh "SELECT MAX(DATE) FROM DATA" nó chỉ trả về duy nhất 1 giá trị của "20200331" trong cột DATE của bảng DATA.
    OT muốn truy vân lấy tất cả các cột và dữ liệu là ngày max như bảng kết quả thì câu lệnh viết sao ạ T_T

    Cảm ơn bạn nhiều.
    Chúc bạn nhiều sức khỏe.

    Hỏi thêm:
    – Data luôn được sắp xếp cột A (Date) từ nhỏ đến lớn?
    – Trong Data có khi nào có ngày lớn hơn ngày hiện tại (Today()) không? Nếu có thì trước 1 ngày hoặc sau 1 ngày hiện tại thì lấy ngày nào?
    – Số 20200299 thể hiện điều gì (Từ dòng 18594)

    Con chào Thầy,
    Cảm ơn Thầy đã quan tâm ạ,
    "20200299 " là do ngày hệ thống (phần mềm) tự sinh ra do người dùng trong quá trình nhập liệu không nhập ngày (nó sẽ lấy tháng năm của tháng hiện tại, còn ngày mặc định là 99)
    Trong data không có ngày lớn hơn hiện tại Thầy ạ.

    Như vậy lúc nào ngày ở dòng cuối luôn là ngày gần nhất.
    Cứ Filter ngày này là ra thôi.

    Con chào Thầy
    Con sử dụng câu lệnh "SELECT * FROM DATA" trong hệ thống xuất dữ liệu ra thì dữ liệu trong cột ngày sắp xếp y chang bảng data Thầy ạ (nguyên xi chưa chỉnh sửa gì ạ).
    Nhìn vào "20200299" và "20200331" nó khác nhau 6 ký tự đầu về tháng/định dạng hệ thống là "yyymmdd1" có số 1 cuối cùngThầy ạ.
    Nếu sử dụng VBA để lọc từ DATA sang sheet KQ giống như bài 74 thì cũng xử lý chuỗi ngày tháng này Thầy ạ.
    Con cảm ơn Thầy.

    Câu truy vấn là Select * from data where date=(select….)
    Chính là biến s ở bài của mình.

    A! Được rồi. Cảm ơn Bạn nhiều ^_^ ,, động đến cái SQL này cũng rắc rối nhỉ hic,,,
    Dữ liệu không may có hư chắc mình ốm đòn với các Sếp quá!

  12. hands says:

    Nếu có "20200331" và "20200399" ngày gần nhất là ngày nào?

    Con chào Bác Hiếu,
    Cảm ơn Bác Hiếu đã quan tâm ạ.
    Trường hợp này con chưa gặp mà hệ thống cũng chỉ cho phép 1 tháng chỉ có một ngày
    Vì đây là số liệu trong bản kiểm kê 1 tháng chỉ sảy ra 1 lần không có 2 ngày như vậy Bác ạ.
    Chắc là cứ số nào to hơn thì là gần nhất thôi ạ. Híc.
    Híc tháng 12 nó có 2 ngày luôn,, hình như tháng đó mới đưa data vào phần mềm nên nó vậy ạ T_T

    Có 2 ngày "20200229" và "20200299"

    Vâng Bác, do tháng 2 không nhập thông tin ạ .
    Nghĩa là có 2 ngày thật , còn về nguyên tắc thì 1 tháng chỉ có một ngày thôi ạ T_T

    Ví dụ dữ liệu chỉ tới tháng 2 năm 2020, ngày cuối sẽ là ngày nào?

    Hic,con cũng không biết Bác ạ, nó còn tùy thuộc vào ngày mọi người thực hiện kiểm kê Bác ạ, thông thường là ngày cuối tháng cũng có thể là trước đó. Nếu chỉ có đến tháng 2 – loại bỏ ngày xx99 lấy ngày lớn nhất trong tháng 2 nếu không có thì mới lấy ngày 99 trong tháng 2 Bác ạ.

    Rắc rối rồi, SQL bài trước phải viết lại

    Hic,dạ vâng có thể trong trường hợp dữ liệu con đưa lên là đúng nhưng trường hợp như Bác đang đề cập là gặp vấn đề ạ.
    Nếu vậy thì câu lệnh SQL phải sửa lại sao vậy Bác.

    Thử code

    Sub ABC()
      Dim cn As Object, rs As Object, SQL As String
    
    Set cn = CreateObject("ADODB.Connection")
      cn.Open ("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & ThisWorkbook.FullName & ";Extended Properties=""Excel 12.0;HDR=No""")
      SQL = "Select * From [DATA$] Where f1 = (Select Max(IIf(Mid(f1,7,2)=99,Mid(f1,1,6)&""00"",f1)) From [DATA$])"
      Set rs = cn.Execute(SQL)
      If Not rs.EOF() Then
        Sheets("sheet1").Range("A2").CopyFromRecordset rs
      End If
      rs.Close:    Set rs = Nothing
      cn.Close:    Set cn = Nothing
    End Sub

    Nếu lấy năm và tháng lớn nhất, trường hợp trong tháng đó chỉ có ngày 99 thì lấy ngày đó nếu có cả ngày khác trong tháng thì lấy max của ngày khác, câu truy vấn có thể dùng:

    SELECT *
    FROM DATA
    WHERE  date= (SELECT IIF(RIGHT(a,2)="00",a+99,a)
                  FROM (SELECT MAX(IIF(RIGHT(date,2)="99",date-99,date)) AS a
                         FROM DATA));

    Mục đích biến các ngày dạng yyyymm99 thành yyyymm00 để tìm max, tìm được max rồi nếu là ngày tháng bình thường thì giữ nguyên không thì chuyển lại về dạng yyyymm99.

    Cái này phải bắt buộc nhập chuẩn dữ liệu từ phần mềm, chứ chẳng ai để như thế này cả, rất khó xử lý sau này.

    Xin chào quanluu1989
    Cảm ơn Bạn đã góp ý, phần mềm này của công ty OT cũng đang trong quá trình vừa thiết kế vừa sử dụng ạ, nên cũng có thể sẽ khó tránh khỏi lỗi thiết kế ạ.
    Cảm ơn Bác HieuCD và Bạn Hau151978 nhiều ạ, đã cho OT 2 cách làm trên Excel và trong SQL.
    Ngày mai đến cơ quan OT sẽ giả lập tình huống lỗi như đã đề cập để test thử ạ, nếu có vấn đề gì OT sẽ thông tin lại ở đây ạ.
    Chúc mọi người nhiều sức khỏe, thân nhiện luôn luôn nằm trong tiêu chuẩn trong mùa chống dịch này ạ.

  13. hands says:

    Xin chào Bác HieuCD và Bạn Hau151978
    OT thử tạo tình huống lỗi sảy ra,sửa ô A28205:
    20200331 thành 20200499
    Thì dữ liệu lọc sang sheets("KQ")
    Code cho sub B ok, cho sub ABC chưa được ạ.

    Sub B()
        'Hau151978
        Dim cnn As New ADODB.Connection
        Dim rs As New ADODB.Recordset
        Dim s As String
        s = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & ThisWorkbook.FullName & " ;Extended Properties=""Excel 12.0 Xml;HDR=YES"";"
        cnn.Open s
        s = "SELECT * FROM [DATA$] " & _
            "WHERE DATE = (SELECT IIF(RIGHT(a,2)=""00"",a+99,a) " & _
            "FROM (SELECT MAX(IIF(RIGHT(DATE,2)=""99"",DATE-99,DATE)) AS a " & _
            "FROM [DATA$]));"
    
    rs.Open s, cnn
        Sheets("KQ").Range("A2:K10000").ClearContents
        Sheets("KQ").Range("A2").CopyFromRecordset rs
        rs.Close
        Set rs = Nothing
        cnn.Close
        Set cnn = Nothing
    End Sub
    
    Sub ABC()
    
    'HieuCD
        Dim cn As Object, rs As Object, SQL As String
    
    Set cn = CreateObject("ADODB.Connection")
        cn.Open ("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & ThisWorkbook.FullName & ";Extended Properties=""Excel 12.0;HDR=No""")
        SQL = "Select * From [DATA$] Where f1 = (Select Max(IIf(Mid(f1,7,2)=99,Mid(f1,1,6)&""00"",f1)) From [DATA$])"
    
    Set rs = cn.Execute(SQL)
        Sheets("KQ").Range("A2:K10000").ClearContents
        If Not rs.EOF() Then
    
    Sheets("KQ").Range("A2").CopyFromRecordset rs
        End If
    
    rs.Close:    Set rs = Nothing
        cn.Close:    Set cn = Nothing
    
    End Sub

    Chỉnh lại

    Sub ABC()
      Dim cn As Object, rs As Object, SQL As String
    
    Set cn = CreateObject("ADODB.Connection")
      cn.Open ("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & ThisWorkbook.FullName & ";Extended Properties=""Excel 12.0;HDR=no""")
      SQL = "Select * From [DATA$] Where IIf(Mid(f1,7,2)=99,Mid(f1,1,6)&""00"",f1) = (Select Max(IIf(Mid(f1,7,2)=99,Mid(f1,1,6)&""00"",f1)) From [DATA$])"
      Set rs = cn.Execute(SQL)
      If Not rs.EOF() Then
        Sheets("sheet1").Range("A2").CopyFromRecordset rs
      End If
      rs.Close:    Set rs = Nothing
      cn.Close:    Set cn = Nothing
    End Sub

    Xin chào Bác HieuCD,
    Code trên của Bác con thử chạy được rồi ạ, cảm ơn Bác Hiếu.

    @OT trong code B nếu chạy trên máy mình sẽ không ra kết quả, không biết sao máy bạn lại chạy được
    s = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & ThisWorkbook.FullName & " ;Extended Properties=""Excel 12.0 Xml;HDR=YES"";"
    chỗ "Excel 12.0 Xml" chỉ đúng khi lấy dữ liệu file xlsx còn xlsm là Excel 12.0 macro hoặc Excel 12.0, xlsb là Excel 12.0
    Code ABC kia chưa đúng vì khi max là ngày 99 thì Select Max(IIf(Mid(f1,7,2)=""99"",Mid(f1,1,6)&""00"",f1)) From [DATA$] sẽ ra ngày 00, lúc đó sẽ không tìm được record nào có ngày đó.

    Xin chào Hau151978,
    Khi đọc thông tin trên của Bạn , OT thấy trong cùng thư mục xlsm vẫn còn tập tin xlsx , OT thử xóa xlsx đi thậm trí di truyển tập tin xlsm sang một thư mục mới và chạy sub B vẫn có kết quả.
    OT không hiểu ADO sau khi chạy lần đầu tiên nó có lưu dữ liệu đâu không nữa T_T
    2512

    Cũng có thể do may mắn, máy mình thử chạy 3 lần không được, xóa chữ xml đi là được ngay. Tốt nhất cứ theo hướng dẫn [URL='www.connectionstrings.com/excel-2007/']ở đây thôi.

    Xin chào Hau151978,
    Cảm ơn Bạn, OT đã sửa lại câu lệnh connection strings theo link bạn gửi kèm rồi ạ.
    Kết quả vẫn lấy được số liệu.. hay thậy có thể do OT sử dụng phiên bản Office 365 nên có gì đó khác khác.
    OT chưa thử các phiên bản office khác.

    Mình cũng dùng 365, vừa thử khởi động lại máy thì thấy lại chạy được với Excel 12.0 xml. Chắc lúc trước máy mình có vấn đề.

  14. hands says:

    Xin chào các Bạn,

    Nhờ các Bạn giúp đỡ OT sử dụng ADO/SQL để thực hiện trường hợp sau với ạ:
    Từ bảng "DULIEU" lọc duy nhất 3 cột MAHANG,TENHANG,CHUNGLOAI,sau đó tính tổng số lượng theo vùng công thức màu hồng tím dựa vào điều kiện từ ngày đến ngày. Kết quả mong như bảng "TH_MH" ạ.
    OT sử dụng câu lệnh "SELECT DISTINCT MAHANG,TENHANG,CHUNGLOAI" thì lấy được dữ liệu duy nhất của 3 trường, nhưng chưa biết cách tính toán cho vùng công thức màu hồng ạ.

    25152516

    Không biết ADO, viết bằng VBA được không?

    Con chào Thầy ạ,
    Dạ được chứ Thầy, vì hiện giờ con đang sử dụng công thức ạ. Về dạng dữ liệu kiểu này con đang sử dụng nhiều, Vba có thể ứng dụng đư nhiều tình huống khác ạ.
    Con muốn hỏi thêm phương pháp ADO/SQL để lấy dữ liệu trực tiếp từ SQL_Server mà không cần phải đưa dữ liệu về Excel nữa .
    Con cảm ơn Thầy nhiều ạ,
    Chúc Thầy nhiều sức khỏe ạ.

    Bạn tìm hiểu group by nhé. Mình khuyên là nên viết procedure ở phía server, code vba chi để truyền tham số và thực thi thôi.
    vi du:
    select mahang, sum(line_01)
    from table
    group by mahang

    Xin chào quanluu1989,
    Cảm ơn Bạn đã góp ý, đúng là khi lấy một lượng dữ liệu lớn từ máy chủ về thì nên thực hiện trực tiếp qua Sever ạ.
    Vba trong trường hợp này vẫn có thể sử dụng được ạ,nếu dữ liệu không quá nhiều(khoảng hai trăm nghìn dòng trở xuống- do số lượng bản ghi đã khống chế từ câu lệnh lấy dữ liệu từ mày chủ).
    Nhưng để đảm bảo có thể tổng hợp dữ liệu ở một khoảng thời gian dài thì việc sử dụng vba có thể sẽ bị treo do dữ liệu lấy về quá nhiều ạ.
    Với ví dụ trên OT chưa hiểu và nắm bắt được phiền bạn giúp đỡ OT làm viết câu lệnh select theo tất cả điều kiện tín toán trong cột "TONG CONG" được không ạ.
    Cảm ơn Bạn nhiều.

    Vấn đề không hẳn là dữ liệu lớn vì dùng procedure chưa chắc nhanh hơn, mà quan trọng ở bảo mật, vì người dùng biết bạn đang truy vấn bảng nào, nếu người ta có quyền write thì người ta có thể làm nhiều việc xấu.

    Còn query như sau:
    SELECT MAHANG,TENHANG,CHUNGLOAI , sum(TONGCONG) AS TONGCONG
    FROM TABLE
    WHERE NGAY BETWEEN TU_NGAY AND DEN_NGAY
    GROUP BY MAHANG,TENHANG,CHUNGLOAI

    Bạn thử code sau nhé:

    Sub TongHopDL_HLMT()
        Dim strDkNgay As String, strSQL As String, strLine As String, strGroup As String
        strDkNgay = " NGAYTHANG BETWEEN #2020-03-26# AND #2020-04-13# "
        strGroup = " MAHANG,TENHANG,CHUNGLOAI "
        strLine = " Val(iif(isnull(LINE_01),0,LINE_01)) as LINE_01,Val(iif(isnull(LINE_02),0,LINE_02)) as LINE_02,Val(iif(isnull(LINE_03),0,LINE_03)) as LINE_03,Val(iif(isnull(LINE_04) ,0,LINE_04))  as LINE_04 " & _
                  ",Val(iif(isnull(LINE_05),0,LINE_05)) as LINE_05,Val(iif(isnull(LINE_06),0,LINE_06)) as LINE_06,Val(iif(isnull(LINE_07),0,LINE_07)) as  LINE_07,Val(iif(isnull(LINE_08),0,LINE_08)) as LINE_08 "
        strSQL = "Select " & strGroup & ",0 as SOLUONG,0 AS SL_KT,0 AS SL_LB," & strLine & " From [DULIEU$] WHERE " & strDkNgay & " AND CONGDOAN LIKE '%CD%' AND PHANLOAI LIKE '%LB%'"
        strSQL = strSQL & " Union all Select " & strGroup & ",SOLUONG ,0,0,0,0,0,0,0,0,0,0 From [DULIEU$] WHERE " & strDkNgay & " AND CONGDOAN LIKE '%CD%'"
        strSQL = strSQL & " Union all Select " & strGroup & ",0,SOLUONG,0,0,0,0,0,0,0,0,0 From [DULIEU$] WHERE " & strDkNgay & " AND CONGDOAN LIKE '%CD%' AND PHANLOAI LIKE 'KHXX001'"
        strSQL = strSQL & " Union all Select " & strGroup & ",0,0,SOLUONG,0,0,0,0,0,0,0,0 From [DULIEU$] WHERE " & strDkNgay & " AND CONGDOAN LIKE '%CD%' AND PHANLOAI LIKE '%LB%'"
        With CreateObject("ADODB.Connection")
            .Open ("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & ThisWorkbook.FullName & ";Extended Properties=Excel 12.0")
            Sheet1.Range("A2").CopyFromRecordset .Execute("Select " & strGroup & ",Sum(SoLuong),Sum(SL_KT),Sum(SL_LB),Sum(SL_LB)/Sum(SoLuong),Sum(LINE_01),Sum(LINE_02),Sum(LINE_03),Sum(LINE_04),Sum(LINE_05),Sum(LINE_06),Sum(LINE_07),Sum(LINE_08) From ( " & strSQL & ") Group by " & strGroup)
        End With
    End Sub

    OT cảm ơn anh Hai Lúa Miền Tây & quanluu1989,
    Hiện code của anh Hai Lúa OT đã test trên Excel OK rồi ạ, còn code của Bạn quanluu1989 OT chưa có điều kiện để làm thử trong SQL Server, khi có kết quả OT sẽ thông tin lại ạ.
    Kính chúc GPE nhà mình đầu tháng vui vẻ & thành công.

    Sao không dùng OR anh ơi

    strDkNgay & " AND CONGDOAN LIKE '%CD%' AND (PHANLOAI LIKE '%LB%'"OR PHANLOAI LIKE 'KHXX001'")

    Dựa vào file của chủ thớt rồi làm theo từng đoạn thôi bạn. Ý tưởng là thế, còn sử dụng phải tùy biến.

  15. hands says:

    Xin chào tất cả mọi người,
    Hiện OT đang có một đoạn code bên dưới để lấy dữ liệu từ bảng "Orders" từ SQL đưa về Excel tại Worksheets("Sheet1").Range("A2:Z500")
    OT muốn lấy thêm một bảng nữa cùng Database với "Orders" ví dụ "Produtions" , thì câu lệnh là "SELECT * FROM Produtions" để đưa vào Worksheets("Sheet1").Range("AA2:AH5000")

    Xin mọi trợ giúp OT làm thế nào để có thể chạy 2 câu lệnh truy vấn này chỉ một lần chạy kết nối, nghĩa là đưa cả 2 bảng "Orders" và "Produtions"
    Vào 2 vùng dữ liệu khác nhau khi chạy Sub ADOExcelSQLServer?

    Sub ADOExcelSQLServer()
    
    Dim Cn As ADODB.Connection
        Dim Server_Name As String
        Dim Database_Name As String
        Dim User_ID As String
        Dim Password As String
        Dim SQLStr As String
        Dim rs As ADODB.Recordset
        Set rs = New ADODB.Recordset
    
    Server_Name = "Server_Name" ' Enter your server name here
        Database_Name = "DB_TEST" ' Enter your  database name here
        User_ID = "Sa" ' enter your user ID here
        Password = "246357" ' Enter your password here
    
    SQLStr = "SELECT * FROM Orders" ' Enter your SQL here
    
    Set Cn = New ADODB.Connection
        Cn.Open "Driver={SQL Server};Server=" & Server_Name & ";Database=" & Database_Name & _
        ";Uid=" & User_ID & ";Pwd=" & Password & ";"
    
    rs.Open SQLStr, Cn, adOpenStatic
    
    With Worksheets("Sheet1").Range("A2:Z500")
            .ClearContents
            .CopyFromRecordset rs
        End With
    
    rs.Close
        Set rs = Nothing
        Cn.Close
        Set Cn = Nothing
    End Sub

    Em lặp lại việc gán câu lệnh SQL cho recordset mới trước khi đóng cn.

    Sub ADOExcelSQLServer()
    
    ...
    
    SQLStr = "SELECT * FROM Orders" ' Enter your SQL here
    
    Set Cn = New ADODB.Connection
        Cn.Open "Driver={SQL Server};Server=" & Server_Name & ";Database=" & Database_Name & _
        ";Uid=" & User_ID & ";Pwd=" & Password & ";"
    
    rs.Open SQLStr, Cn, adOpenStatic
    
    With Worksheets("Sheet1").Range("A2:Z500")
            .ClearContents
            .CopyFromRecordset rs
        End With
    
    'Thêm đoạn này.
    SQLStr = "SELECT * FROM Production"
    rs.Open SQLStr, Cn, adOpenStatic
    
    With Worksheets("Sheet1").Range("AA2:AH5000")
            .ClearContents
            .CopyFromRecordset rs
        End With
    
    rs.Close
        Set rs = Nothing
        Cn.Close
        Set Cn = Nothing
    End Sub

    Xin chào ongke0711,
    Đúng rồi Anh rồi anh, vậy mà OT không nghĩ ra nên cứ để việc kết nối đóng & lặp lại nhiều lần.
    Cảm ơn Anh nhiều ạ.

    Có vẻ bạn cần tìm hiểu rõ SQL và ADO thêm chút là biến tấu được, khỏi nhờ diễn đàn chi cho vất vả

    Cảm ơn Bạn, không có diễn đàn không có các Thầy/Bác/Anh/Chị và các Bạn ở đây là mình chết chắc đó 😀

    Đây là lý do trong mấy file làm demo lúc trước, anh luôn có hàm tạo kết nối (connection) riêng và hàm lấy Recordset riêng chứ không gộp chung vô cái hàm như của em hiện tại.
    – Khi khởi chạy sẽ gọi hàm kết nối rồi để đó.
    – Lấy recordset các kiểu, sau khi xong mới đóng kết nối.
    Với hàm kết nối độc lập, trong vài trường hợp khi xử lý recordset cần ngắt kết nối tạm thời thì vẫn có thể chủ động ngắt rồi tạo lại sau khi xử lý xong Recordset, cập nhật về hệ thống.

  16. hands says:

    Xin chào các Bạn,
    Hiện OT đang sử dụng câu lệnh truy vấn lấy dữ liệu trong SQL từ ngày đến ngày thì được dữ liệu như bảng 1 (các mã hàng thể hiện chi tiết theo từng ngày).
    Giờ OT mong muốn cũng câu lệnh truy vấn từ ngày đến ngày này nhưng số lượng cho từng mã hàng được thể hiện theo từng tháng (cộng gộp các ngày trong tháng) như bảng 2.
    Thì câu lệnh truy vấn được viết thế nào ạ?

    Em dùng hàm Format, Code sau chưa đưa điều kiện lọc, em tự thêm vào nhé.

    Sub TongHopDL_HLMT()
        With CreateObject("ADODB.Connection")
            .Open ("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & ThisWorkbook.FullName & ";Extended Properties=Excel 12.0")
            Sheet1.Range("M6").CopyFromRecordset .Execute("Select [CODE],format([DATE],'mm-yyyy'),SUM(QTY) from [Sheet1$A5:C15000] Group by [CODE],format([DATE],'mm-yyyy')")
    
    End With
    End Sub

    Cảm ơn anh Hai Lúa nhiều ạ, câu lệnh đúng ý OT ạ.
    OT đang tìm hiểu câu lệnh SELECT SUM..GROUP BY.. nhưng cách của anh đơn giản hơn & dễ ứng dụng hơn ạ.

  17. hands says:

    Xin chào các Bạn,
    Các Bạn cho OT hỏi có cách nào thiết lập trong SQL-Database để truy vấn từ ứng dụng Excel đến nó chỉ sử dụng được duy nhất câu lệnh Select không thôi?

    Em nên tạo các Store Procedure, View rồi sau đó gọi nó thôi.

    Xin chào anh Hai Lúa,
    Cảm ơn anh đã gợi ý cho OT.
    Có phải OT hiểu như thế này không ạ, nghĩa là không có cách nào khác khi truy vấn trực tiếp đến bảng gốc
    mà phải tạo một bản sao cho bảng đó trong SQL và từ Excel sẽ gọi bảng bản sao này phải không anh?

    Hiểu nôm na Store Procedure giống như 1 hàm, ví dụ như hàm trong Excel. Khi cần tìm hay lấy dữ liệu ra bảng khác thì bạn phải vào bảng gốc để tìm, lọc… rồi lấy kết quả đưa chỗ khác. Nhưng bạn dùng hàm thì không phải thao tác trực tiếp trên bảng gốc. Hàm sẽ lấy dữ liệu thảo điều kiện.
    Quay lại Store Procedure hơn chỗ là nó có thể thực hiệc các lệnh thêm, sửa, xóa vào dữ liệu bảng gốc.

    Cảm ơn anh Hai Lúa
    OT sẽ tìm hiểu thêm về Store Procedure ạ.
    Vấn đề của OT là làm sao để ko cho phép từ ứng dụng Excel ngoài sử dụng câu lệnh select ra thì không được sử dụng câu lệnh khác ấy ạ.
    Ví dụ: Từ Excel OT đang sử dụng câu lệnh "Select * from ODER" nếu OT sử dụng câu lệnh khác Select thì sẽ không được ạ.
    Nếu sửa code trong Excel thì có thể được nhưng đã là code trong Excel rồi thì vẫn có thể bỏ đi được nếu người nào hiểu code.
    OT muốn hỏi cách thiết lập trong SQL để người dùng có sửa code trong Excel thế nào thế nào đi nữa thì cũng chỉ sử dụng được câu lệnh Select thôi ạ.
    Không biết là có thể khắc phục được vde này không anh vì nó liên quan đến vấn đề bảo mật anh ạ.
    Cảm ơn anh Hai Lúa

    Nếu em có quyền trong SQL Server Management Stufio thì em vô folder Security -> Login -> Tạo user và thiết lập "User Mapping" tới cái Database mà em muốn chỉ cho dùng "SELECT.." là: db_datareader

    Xin chào anh ongke0711,
    Cảm ơn anh đã quan tâm và giúp đỡ,hình như đúng là cái này rồi ạ.
    Phiền anh ongke0711 có thể hướng dẫn OT chi tiết một chút được không ạ, OT được phép đụng đến SQL mới đầu cảm thấy hứng thú nhưng sau khi biết thêm một chút thì lại thấy nó cũng nguy hiểm nên thấy lo lắng anh ạ.

    Em tạo User Login rồi test xem User đó hoạt động như thế nào, không ảnh hưởng gì đâu. Tạo một Database tạm rồi thử nghiệm trên database đó thôi. Trong hình cái database "abc" là database tạm.2523252225212520

    Xin cảm ơn ongke0711 nhiều ạ,
    Hướng dẫn bằng hình anh thế này rất dễ hiểu.
    Muốn chỉ cho dùng "SELECT.." là: chỉ cần chọn "db_datareader" ạ Anh?
    vậy còn các db_ còn lại là những gì vậy Anh?

    Em kiếm tài liệu đọc thêm nhe. Cái vụ phân quyền của SQL Server nó rộng lắm, anh không hướng dẫn nỗi đâu.. 🙂

    Xin chào ongke0711,
    reader này chắc là đúng rồi theo OT hiểu chỉ là dạng readonly chỉ đọc không ghi 😀
    OT sẽ thử ạ, cảm ơn anh nhiều.

    Stored Procedure (1) chứ không phải Store Procedure (2).

    (1) Tiếng Anh, trạng thái past participle được dùng cho tình huống thụ động (được/bị). Ở đây nói cái procedure (phương thức) được stored (lưu trữ/chứa) sẵn.

    (2) cũng trong tiếng Anh, trường hợp 2 này thì Store là danh tự được dùng như tĩnh để bổ nghĩa cho danh tự đi tiếp theo. Và vì hai tự được hiểu theo danh cho nên từ thứ nhất là cửa hàng (kho), và từ thứ hai là quy trình. Dịch hai từ là quy trình cửa hàng.

    Từ "thủ tục thường trú" biến thành "qui trình cửa hàng" chỉ vì 1 ký tự. 🙂

  18. hands says:

    Xin chào ongke0711,
    Theo hướng dẫn của Anh, OT đã tạo được một UserName đăng nhập mới và để quyền cho các DB là "db_datareader " giờ công việc còn lại là test
    Anh có thể chỉ cho OT câu lệnh thêm mới 1 dòng dữ liệu hoặc chỉnh sửa / xóa một dòng dữ liệu bất kỳ trong bảng ODER này được không ạ.
    Chỉ cần câu lệnh viết thẳng trong SQL luôn không cần phải qua Excel đâu ạ.
    Cảm ơn Anh nhiều ạ.
    2524
    A! OT thấy rồi những câu lệnh này tìm kiếm là dễ thấy hihi..

    Thao tác ngay trong SSMS, tạo New query rồi gõ câu lệnh nay vô:

    Insert Into dbo.tblHangHoa (STT,MA_HANG,SOLUONG) Values (4,'MH0006',100)

    Delete From dbo.tblHangHoa Where STT=3

    Xin chào ongke0711,
    OT thử đăng nhập với User vừa mới tạo và thử với câu lệnh sau:
    UPDATE .. SET MA_HANG= 'MH1102' WHERE STT=2;
    Hệ thống báo:
    The UPDATE permission was denied on the object 'ODER', database 'OT_TEST', schema 'dbo'.
    Như vậy là OK rồi phải không Anh :))

    Trời đất, STT (?)
    Còn gì là CSDL LH nữa.

    Đúng rồi OT đã thử chính xác là vậy.
    Khi chuyển User chính thì câu lệnh trên OK, với thông báo "(1 row affected)"
    Sau dó OT kiểm tra lại dữ liệu đã thay đổi, còn với User mới tạo thì không..
    OT Cảm ơn Anh ongke0711 nhiều ạ

    Lúc nãy không để ý vụ này :). Em OT xem lại cách thiết kế Table nhé, không cần mấy cột STT lãng phí này đâu.

    Các bác cho em hỏi làm cách nào để biết số dòng chứa SQL của mình ? Cám ơn các bác

    Định nghĩa thế nào là một dòng?

    Sub test()
    Dim cn As Object, rst As Object
    Dim mySQL As String
    Set cn = CreateObject("ADODB.Connection")
    Set rst = CreateObject("ADODB.Recordset")
    With cn
      .Provider = "Microsoft.Jet.OLEDB.4.0"
       If Val(Application.Version) < 12 Then
        .ConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & ThisWorkbook.FullName & ";Extended Properties=""Excel 8.0;HDR=YES;IMEX=1"";"
        Else
         .ConnectionString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & ThisWorkbook.FullName & ";Extended Properties=""Excel 12.0;HDR=YES;IMEX=1"";"
       End If
      .Open
    End With
    mySQL = "select * from BC_TONG where C5 = '" & (Sheet6.Range("S2")) & "'" & _
    "and C6 = '" & (Sheet6.Range("T2")) & "'" & _
    "and C7 = '" & (Sheet6.Range("U2")) & "'" & _
    "and C8*1 = '" & (Sheet6.Range("V2") * 1) & "'" & _
    "and C9 = '" & (Sheet6.Range("W2")) & "'" & _
    "and C10 = '" & (Sheet6.Range("X2")) & "'" & _
    "and C11 = '" & (Sheet6.Range("Y2")) & "' "
    Set rst = cn.Execute(mySQL)
    Range("o3").CopyFromRecordset rst
    ' tim dong chua mySQL trong du lieu
    Dim i  As Long
    'i = Sheet6.Range("A1:M20000").Find("rst").Row
    Set rst = Nothing: Set cn = Nothing
    
    End Sub

    Bác xem giúp

    mySQL = "select * from BC_TONG where C5 = '" & (Sheet6.Range("S2")) & "'" & _
    "and C6 = '" & (Sheet6.Range("T2")) & "'" & _
    "and C7 = '" & (Sheet6.Range("U2")) & "'" & _
    "and C8*1 = '" & (Sheet6.Range("V2") * 1) & "'" & _
    "and C9 = '" & (Sheet6.Range("W2")) & "'" & _
    "and C10 = '" & (Sheet6.Range("X2")) & "'" & _
    "and C11 = '" & (Sheet6.Range("Y2")) & "' "

    Luật đáng ghi nhớ: khi viết câu string cho lệnh SQL, đừng bao giờ tiết kiệm số dấu cách.
    Và do vậy:
    Mẹo tốt để tránh lỗi khi viết một câu lệnh SQL: luôn luôn mở đầu chuỗi bằng dấu cách. Trừ phi chỗ đó là tham số.

    mySQL = " select * from BC_TONG where C5 = '" & (Sheet6.Range("S2")) & "'" & _
    " and C6 = '" & (Sheet6.Range("T2")) & "'" & _ (dấu cách thêm vào trước and, nhưng không trước dấu nháy ' , vì ' là một phần của tham số)
    " and C7 = '" & (Sheet6.Range("U2")) & "'" & _
    " and C8*1 = '" & (Sheet6.Range("V2") * 1) & "'" & _
    " and C9 = '" & (Sheet6.Range("W2")) & "'" & _
    " and C10 = '" & (Sheet6.Range("X2")) & "'" & _
    " and C11 = '" & (Sheet6.Range("Y2")) & "' "

    Câu lệnh SQL thì tạm ổn bác ạ , nhưng h em muốn tìm vị trí nó nằm ở hàng nào trong bảng BC_TONG , có phương án nào ko bác . Có thể trong mySQL có nhiều dữ liệu , mình biết tất cả số dòng chứa dữ liệu my SQL

    Tôi đã hứa với ban quản trị là tôi không phê phán các câu hỏi có từ viết tắt, nhưng tôi chưa hề nhượng bộ quyền từ chối không trả lới các câu hỏi ấy.

  19. hands says:

    Xin chào các bạn,
    Nhờ các bạn xem & giúp tôi trường hợp sau với ạ:
    Tôi có một dữ liệu ban đầu bảng(sheet) S, dùng câu lệnh SQL nào có thể ra được đầu ra dữ liệu như bảng(sheet) R.
    Tổng hợp số lượng duy nhất theo tại bảng S theo các trường: CODE,INW,OUTW,TYPE
    Nếu TYPE=2 thì bảng R sẽ tạo thêm các trường cột: INW2,OUTW2,QTY2 và nhập các dữ liệu duy nhất theo TYPE=2

    Bảng(sheet) S:
    2526

    Bảng(sheet) R:
    2525

    Dùng UNION ALL với điều kiện TYPE=2 sau đó gom nhóm lại là được nhé.

    Dạ,làm phiền anh Hai Lúa Miền Tây,viết giúp OT cụ thể câu lệnh select này với ạ.
    OT cảm ơn anh ạ.

    Em thử code sau nhé:

    Sub GomDL_HLMT()
        Dim strSQL As String
        strSQL = "Select CODE,INW,OUTW,QTY,'' AS INW2,'' AS OUTW2,0 AS QTY2,TYPE  from [S$] WHERE TYPE=1 UNION ALL Select CODE,'','',0,INW,OUTW,QTY,TYPE  from [S$] WHERE TYPE=2"
        With CreateObject("ADODB.Connection")
            .Open ("Provider=Microsoft.ACE.OLEDB.12.0;Extended Properties=Excel 12.0;Data Source=" & ThisWorkbook.FullName)
            Sheet2.Range("A2").CopyFromRecordset .Execute("Select CODE,INW,OUTW,SUM(QTY),INW2,OUTW2,SUM(QTY2),TYPE FROM (" & strSQL & ") GROUP BY CODE,TYPE,INW,OUTW,INW2,OUTW2")
        End With
    
    End Sub

    Lơi hại , lợi hại quá, ngôn SQL thật tuyệt vời.
    Cảm ơn anh Hai Lúa nhiều ạ

    Nếu như tôi thì tôi làm báo cáo bằng CROSSTAB Query như hình sau:

    2528

    Bởi vì có thể trong cột TYPE có nhiều loại mà ta chưa biết nó là loại nào.

    " CROSSTAB Query " là gì vậy anh Hai Lúa? Nó có select như kiểu câu lệnh bài #142 của anh không ạ?
    Nhìn kết quả đúng là gọn và dễ hiểu hơn báo cáo trên của OT ạ.
    Úi trời,câu lệnh #142 của anh mà OT đưa vào áp dụng thử thêm nhiều trường và thêm các điều kiện lọc ngày,tháng,mã hàng…. mà câu lệnh dài quá 😀
    Cảm ơn anh Hai Lúa đã giúp đỡ.

    Do vậy anh mới nói phải dùng Crosstab Query như bài #144, em thử code như sau nhé:

    Sub GomDL_HLMT1()
        With CreateObject("ADODB.Connection")
            .Open ("Provider=Microsoft.ACE.OLEDB.12.0;Extended Properties=Excel 12.0;Data Source=" & ThisWorkbook.FullName)
            Sheet2.Range("A7").CopyFromRecordset .Execute("TRANSFORM SUM(QTY) SELECT CODE,INW,OUTW,SUM(QTY) FROM [S$] GROUP BY CODE,TYPE,INW,OUTW PIVOT TYPE")
        End With
    
    End Sub

    Cách này đúng là ngắn hơn cách trên nhiều, bớt được hẳn 2 câu lệnh Select dài ngoằng 😀 .
    Cảm ơn anh đã chỉ cho OT thêm một cách ạ.
    OT chúc anh Hai Lúa sức khỏe tốt.

    Thêm cho em cái tiêu đề vào luôn nhé, bây giờ không cần quan tâm đến cột TYPE có bao nhiêu loại theo chuẩn.

    Sub GomDL_HLMT2()
        Dim fldName, i As Byte
        With CreateObject("ADODB.Recordset")
            .Open ("TRANSFORM SUM(QTY) SELECT CODE,INW,OUTW,SUM(QTY) AS TOTAL_QTY FROM [S$] GROUP BY CODE,TYPE,INW,OUTW PIVOT TYPE"), "Provider=Microsoft.ACE.OLEDB.12.0;Extended Properties=Excel 12.0;Data Source=" & ThisWorkbook.FullName
            For Each fldName In .Fields
                i = i + 1
                Sheet2.Cells(6, i) = fldName.Name
            Next
            Sheet2.Range("A7").CopyFromRecordset .DataSource
        End With
    
    End Sub

    :yahoo:
    Code xịn xò quá , cảm ơn anh nhiều ạ.

    CrossTab Query thường biến động số cột tuỳ theo dữ liệu (cụ thể là cột TYPE trong ví dụ này) do đó tôi thường đưa vô liệt kê đủ các cột TYPE vào câu lệnh SQL để cố định số cột TYPE, không trồi ra thụt vào :).

    "TRANSFORM SUM(QTY) SELECT CODE,INW,OUTW,SUM(QTY) AS TOTAL_QTY FROM [S$] GROUP BY CODE,TYPE,INW,OUTW PIVOT TYPE IN (1,2,3,4,5) "
  20. hands says:

    Xin chào các bạn,
    OT có 2 bảng dữ liệu ban đầu TDK(tồn đầu kỳ) & bảng NX (nhập xuất),nhờ các bạn xem và giúp đỡ câu lệnh truy vấn SQL để ra kết quả tại bảng NXT ạ:

    1.Bảng TDK:
    2532
    2.Bảng NX:
    2531

    Kết quả mong muốn sau khi thực hiện câu lệnh truy vấn SQL ra kết quả như bảng NXT ạ:
    2530

    Tôi có thể dùng power pivot cho bài này không bạn!

    Xin chào bạn excel_lv1.5,
    Cảm ơn bạn đã quan tâm đến bài viết này của OT.
    Dạ vâng bạn có thể sử dụng power pivot, hoặc Vba đều được ạ… các cách này sẽ giúp ích để OT tham khảo và ứng dụng sang vấn đề khác ạ.

    Còn với trường hợp này OT cần câu lệnh truy vấn để lấy dữ liệu từ DB về ạ.
    Rất mong được bạn và mọi người giúp đỡ.

    Ah, tôi hiểu nhầm vấn đề, mà bạn connect với DB SQL Server hay Excel!

    Dạ OT kết nối với DB SQL Server ạ.
    OT chỉ cần câu lệnh truy vấn và không lệnh kết nối và đóng kết nối đến DB nữa ạ.
    Ví dụ như bài 142 ở trên OT có thể lấy câu lệnh "Select…" để sử dụng ạ.
    còn các câu lệnh kết nối ADO trong excel mang tính test kết quả thay vì trên môi trường SQL thì test trên Excel ạ.

    Tôi không có dữ liệu để test nên không biết chạy được không, nếu SQL thì tôi viết vầy (giả sử bạn có một bảng danh mục hàng )

    select a.[ma_hang],
    sum(b.[so_luong]) [tdk],
    sum(case when c.[kieu]='n' then c.[so_luong] else 0 end) [nhap],
    sum(case when c.[kieu]='x' then c.[so_luong] else 0 end) [xuat],
    sum(b.[so_luong])+sum(case when c.[kieu]='n' then c.[so_luong] else 0 end)-sum(case when c.[kieu]='x' then c.[so_luong] else 0 end)  [ton]
    from [category] a
    left join [TDK] b on a.[ma_hang]=b.[ma_hang]
    left join [NX] c on a.[ma_hang]=c.[ma_hang]
    group by a.[ma_hang]
    order by a.[ma_hang]

    Cảm ơn bạn nhiều, đúng như vậy OT chỉ cần câu lệnh truy vấn trong SQL dạng này ạ.

    Úi nghĩa là phải cần thêm một bảng danh mục mã hàng để tham chiếu qua ạ.
    Xin lỗi OT sơ ý cứ nghĩ sử dụng câu lệnh where mã hàng =N'.. '
    Giờ OT mới hiểu thêm nếu lấy tất cả mã hàng là phải có thêm bảng danh mục mã hàng.
    Cảm ơn bạn nhiều, ngày mai có điều kiện OT sẽ thử và thông tin lại ở đây ạ.
    Chúc bạn ngủ ngon.

  21. hands says:

    Không cần bảng danh mục hàng được không bạn 🙂

    Con chào Bác HieuCD,
    Bác ngủ muộn vậy ạ. 😀
    Bác có câu lệnh nào khác, Bác cho con tham khảo thêm với.
    Con cảm ơn Bác HieCD.

    Thường thì trong DB sẽ có bảng danh mục, muốn tạo riêng thì có thể tạo từ 2 bảng và

    select DISTINCT a.[ma_hang]
    into #category
    from
    (
        select a.[ma_hang]
        from [TDK] a
        union all
        select a.[ma_hang]
        from [NX] a
    ) a

    Cảm ơn bạn nhiều ạ,
    Đúng là DB có bảng danh mục hàng thường gọi là chung là masterItem ạ.
    Giả sử không sử dụng bảng danh mục hàng có sẵn đó và nếu gộp chung câu lệnh này lên cùng với câu lệnh tính tồn kho thì câu lệnh mới sẽ sửa như thế nào vậy bạn?

    Thực ra vẫn còn một cách làm khác là join 2 bảng TDK và NX lại rồi pivot
    Bạn copy 2 câu đó lại chạy chung là được, câu tạo Category chạy trước

    Ôi, bạn biết cách nào thì chỉ thêm cho OT với ạ, cách nào dễ ứng dụng và xuất ra được báo cáo theo nhu cầu gần gũi và dễ dàng sử dụng thì OT sẽ tham khảo để ứng dụng ạ.

    OT đã từng thử chạy nhiều câu lệnh truy vấn cùng lúc nhưng đó là trong SQL, mỗi câu lệnh select độc lập sẽ cho ra kết quả của mỗi bản, như vậy nhiều câu select riêng sẽ cho ra nhiều bảng dữ liệu kết quả. Còn trong code Excel sau khi thực hiện câu lệnh kết nối đến DB thì có thể chạy kiểu đó được không ạ, hay phải ghép chúng lại thành một chuỗi.
    sáng mai đến cơ quan OT mới có điều kiện để thử được ạ.
    Cảm ơn bạn nhiều.

    Trong ADO hình như việc ghép 2 câu lệnh như vậy chạy không được , bạn có thể test thông qua connect SQL của excel được nó cho phép ghép 2 câu lệnh như vậy
    Đây là trường hợp dùng pivot (tôi cũng chưa test chỉ viết trên word)

    select a.[kieu],a.[ma_hang], a.[so_luong]
    from
    (
        select 'TDK' [kieu],a.[ma_hang], a.[so_luong]
        from [tdk] a
        union all
        select a.[kieu],a.[ma_hang], a.[so_luong]
        from [nx] a
    ) a
    pivot
    (
    sum([so_luong])
    for [kieu] in ('tdk','n','x')
    ) b

    Dạ
    Cảm ơn bạn rất nhiều, OT cũng nghĩ trong codeExcel không chạy kiểu đó lên bài viết phía trên OT có sửa lại nói rõ hơn khi nhiều câu lệnh truy vấn cùng lúc trong SQL.
    Cảm ơn bạn nhiểu những kiến thức thức rất cần thiết cho OT mà OT không thể học và nhớ được.
    OT ngủ đây ạ, chúc bạn và mọi người ngon giấc.

    Xin chào excel_lv1.5
    OT đã thêm bảng DMHH (danh mục hàng)
    2535
    Và thử code bài #156 & #164 thì cả 2 code chạy đều bị lỗi, nhờ các bạn xem & giúp đỡ OT với ạ:

    'Error: Unrecognized keyword WHEN.
    Sub Xuat_Nhap_Ton_SQL()
        Dim strSQL As String
        strSQL = "select a.[ma_hang]," & _
                "sum(b.[so_luong]) [tdk]," & _
                "sum(case when c.[kieu]='n' then c.[so_luong] else 0 end) [nhap]," & _
                "sum(case when c.[kieu]='x' then c.[so_luong] else 0 end) [xuat]," & _
                "sum(b.[so_luong])+sum(case when c.[kieu]='n' then c.[so_luong] else 0 end)-sum(case when c.[kieu]='x' then c.[so_luong] else 0 end)  [ton] " & _
                "from [DMHH] a " & _
                "left join [TDK] b on a.[ma_hang]=b.[ma_hang] " & _
                "left join [NX] c on a.[ma_hang]=c.[ma_hang] " & _
                "group by a.[ma_hang] " & _
                "order by a.[ma_hang]"
        With CreateObject("ADODB.Connection")
            .Open ("Provider=Microsoft.ACE.OLEDB.12.0;Extended Properties=Excel 12.0;Data Source=" & ThisWorkbook.FullName)
            Sheet6.Range("G1").CopyFromRecordset .Execute(strSQL)
        End With
    End Sub
    
    'Error: Syntax error in FROM clause.
    Sub Xuat_Nhap_Ton_SQL_2()
        Dim strSQL As String
           strSQL = "select a.[kieu],a.[ma_hang], a.[so_luong] " & _
                    "from (" & _
                          "select 'TDK' [kieu],a.[ma_hang], a.[so_luong] from [tdk] a " & _
                          "Union all " & _
                          "select a.[kieu],a.[ma_hang], a.[so_luong] from [nx] a " & _
                          ") a " & _
                    "pivot " & _
                    "(Sum ([so_luong])for [kieu] in ('tdk','n','x')) b"
        With CreateObject("ADODB.Connection")
            .Open ("Provider=Microsoft.ACE.OLEDB.12.0;Extended Properties=Excel 12.0;Data Source=" & ThisWorkbook.FullName)
            Sheet6.Range("G1").CopyFromRecordset .Execute(strSQL)
        End With
    End Sub

    Chào Bạn,
    Câu lệnh truy vấn này OT thử trong môi trường SQL, OK rồi ạ.
    Xin hỏi thêm bạn nếu OT muốn truy vấn phân biệt theo một trường nữa (KHO_LUU_TRU) thì câu lệnh này sẽ phải sửa lại như thế nào ạ.
    OT đã bổ sung trường 'KHO_LUU_TRU' vào bảng 'TDK' và 'NX' như ảnh kèm, nhờ bạn và mọi người xem giúp ạ:
    2536

  22. hands says:

    Xin hỏi thêm bạn nếu OT muốn truy vấn phân biệt theo một trường nữa (KHO_LUU_TRU) thì câu lệnh này sẽ phải sửa lại như thế nào ạ.
    OT đã bổ sung trường 'KHO_LUU_TRU' vào bảng 'TDK' và 'NX' như ảnh kèm, nhờ bạn và mọi người xem giúp ạ:

    🙂 Anh góp ý vụ tính tồn kho này chút nhé. Vì xem mấy bài ở trên em đưa dữ liệu đã trích xuất ra theo từng mã hàng rồi nên cách xử lý sẽ khác. Thông thường cái tác vụ báo cáo NXT là: tính NXT của các mã hàng (hoặc 1 mã hàng) trong khoảng thời gian nào đó cần truy vấn. Do đó tốt nhất là em nên đưa cái CSDL để tính NXT có các thông tin: tất cả các mã hàng, ngày tháng nhập xuất, kho; Tồn đầu kỳ của tất cả các mã hàng – kho tồn – tháng/năm tồn (bảng TDK theo tháng phải khong em?). Khi đó câu lệnh truy vấn sẽ đầy đủ hơn, kết quả trả về cũng đáp ứng như cầu nhiều hơn.

    Xin chào anh ongke0711,
    Dạ vâng mới đầu OT cũng nghĩ như vậy nhưng OT thấy để ít trường như vậy để câu lệnh nó ngắn ạ nếu để nhiều trường sợ câu lệnh nó dài rồi rối lên.
    Vậy OT sẽ bổ sung thêm 1 số trường ngày tháng (các trường cơ bản) sau đó gửi lại data lên đây nhờ anh và mọi người xem giúp ạ.
    Cảm ơn anh đã góp ý ạ.
    OT

    Mỗi lần gửi thực thi SQL qua ADO trong Excel thì chỉ gửi 1 câu lệnh mà thôi
    Ghép thì có UNION, JOIN… nhưng đó là ghép thành 1 lệnh

    Muốn nhiều lệnh thì cần gửi thực thi nhiều lần (dùng chung 1 kết nối)

  23. hands says:

    Xin chào excel_lv1.5
    OT đã thêm bảng DMHH (danh mục hàng)
    Và thử code bài #156 & #164 thì cả 2 code chạy đều bị lỗi, nhờ các bạn xem & giúp đỡ OT với ạ:

    'Error: Unrecognized keyword WHEN.
    Sub Xuat_Nhap_Ton_SQL()
        Dim strSQL As String
        strSQL = "select a.[ma_hang]," & _
                "sum(b.[so_luong]) [tdk]," & _
                "sum(case when c.[kieu]='n' then c.[so_luong] else 0 end) [nhap]," & _
                "sum(case when c.[kieu]='x' then c.[so_luong] else 0 end) [xuat]," & _
                "sum(b.[so_luong])+sum(case when c.[kieu]='n' then c.[so_luong] else 0 end)-sum(case when c.[kieu]='x' then c.[so_luong] else 0 end)  [ton] " & _
                "from [DMHH] a " & _
                "left join [TDK] b on a.[ma_hang]=b.[ma_hang] " & _
                "left join [NX] c on a.[ma_hang]=c.[ma_hang] " & _
                "group by a.[ma_hang] " & _
                "order by a.[ma_hang]"
        With CreateObject("ADODB.Connection")
            .Open ("Provider=Microsoft.ACE.OLEDB.12.0;Extended Properties=Excel 12.0;Data Source=" & ThisWorkbook.FullName)
            Sheet6.Range("G1").CopyFromRecordset .Execute(strSQL)
        End With
    End Sub
    
    'Error: Syntax error in FROM clause.
    Sub Xuat_Nhap_Ton_SQL_2()
        Dim strSQL As String
           strSQL = "select a.[kieu],a.[ma_hang], a.[so_luong] " & _
                    "from (" & _
                          "select 'TDK' [kieu],a.[ma_hang], a.[so_luong] from [tdk] a " & _
                          "Union all " & _
                          "select a.[kieu],a.[ma_hang], a.[so_luong] from [nx] a " & _
                          ") a " & _
                    "pivot " & _
                    "(Sum ([so_luong])for [kieu] in ('tdk','n','x')) b"
        With CreateObject("ADODB.Connection")
            .Open ("Provider=Microsoft.ACE.OLEDB.12.0;Extended Properties=Excel 12.0;Data Source=" & ThisWorkbook.FullName)
            Sheet6.Range("G1").CopyFromRecordset .Execute(strSQL)
        End With
    End Sub

    Bạn phải viết theo kiểu Excel, ví dụ

    Sub Xuat_Nhap_Ton_SQL()
        Dim strSQL As String
        strSQL = "select a.[ma_hang]," & _
                "sum(b.[so_luong])  ," & _
                "sum(-(c.[kieu]='N')* c.[so_luong])," & _
                "sum(-(c.[kieu]='X')* c.[so_luong])," & _
                "sum(iif(isnull(b.[so_luong]),0,b.[so_luong]))+sum(-(c.[kieu]='N')*c.[so_luong])-sum(-(c.[kieu]='X')* c.[so_luong])" & _
                "from ([DMHH$] a " & _
                "left join [TDK$] b on a.[ma_hang]=b.[ma_hang]) " & _
                "left join [NX$] c on a.[ma_hang]=c.[ma_hang] " & _
                "group by a.[ma_hang] " & _
                "order by a.[ma_hang]"
        With CreateObject("ADODB.Connection")
            .Open ("Provider=Microsoft.ACE.OLEDB.12.0;Extended Properties=Excel 12.0;Data Source=" & ThisWorkbook.FullName)
            Sheet6.Range("A6").CopyFromRecordset .Execute(strSQL)
        End With
    End Sub

    Chú ý lệnh:
    sum(iif(isnull(b.),0,b.))

    Code chạy được rồi Bác HieuCD, con cảm ơn Bác.
    Nhưng mà sao TDK của con chỉ có 150 mà code chạy ra tận 4800 , Bác xem giúp con với ạ.

    Không hiểu tại sao luôn 🙁
    Bạn @VetMini giải thích dùm mình

    Sub Xuat_Nhap_Ton_SQL()
        Dim strSQL As String
        strSQL = "select a.[ma_hang]," & _
                "max(b.[so_luong])  ," & _
                "sum(-(c.[kieu]='N')* c.[so_luong])," & _
                "sum(-(c.[kieu]='X')* c.[so_luong])," & _
                "max(iif(isnull(b.[so_luong]),0,b.[so_luong]))+sum(-(c.[kieu]='N')*c.[so_luong])-sum(-(c.[kieu]='X')* c.[so_luong])" & _
                "from ([DMHH$] a " & _
                "left join [TDK$] b on a.[ma_hang]=b.[ma_hang]) " & _
                "left join [NX$] c on a.[ma_hang]=c.[ma_hang] " & _
                "group by a.[ma_hang] " & _
                "order by a.[ma_hang]"
        With CreateObject("ADODB.Connection")
            .Open ("Provider=Microsoft.ACE.OLEDB.12.0;Extended Properties=Excel 12.0;Data Source=" & ThisWorkbook.FullName)
            Sheet6.Range("A6").CopyFromRecordset .Execute(strSQL)
        End With
    End Sub

    Con cảm ơn Bác, code chạy ra kết quả đúng ý con rồi.
    Hic, Bác không hiểu chắc con xác định khỏi tìm hiểu luôn ạ :rolleyes:
    Bác ơi câu lệnh này của Bác, 2 cái dấu "()" có gì khác biệt giữa Excel và SQL không ?
    from ([DMHH$] a left join [TDK$] b on a.=b.) left join [NX$] c on a.=c.
    mà con thấy đưa vào SQL nó kỳ quá ạ

    Mình nghỉ nên có () mối liên kết sẽ tường minh hơn

  24. hands says:

    Chào Bạn,
    Câu lệnh truy vấn này OT thử trong môi trường SQL, OK rồi ạ.
    Xin hỏi thêm bạn nếu OT muốn truy vấn phân biệt theo một trường nữa (KHO_LUU_TRU) thì câu lệnh này sẽ phải sửa lại như thế nào ạ.
    OT đã bổ sung trường 'KHO_LUU_TRU' vào bảng 'TDK' và 'NX' như ảnh kèm, nhờ bạn và mọi người xem giúp ạ:

    Vậy bên bảng DMHH bảng cũng phải có kho luôn nhe bạn, tôi sửa lại trong môi trường SQL Server và khi DMHH có thêm cột kho

    select a.[ma_hang],a.[KHO_LUU_TRU],
    sum(b.[so_luong]) [tdk],
    sum(case when c.[kieu]='n' then c.[so_luong] else 0 end) [nhap],
    sum(case when c.[kieu]='x' then c.[so_luong] else 0 end) [xuat],
    sum(b.[so_luong])+sum(case when c.[kieu]='n' then c.[so_luong] else 0 end)-sum(case when c.[kieu]='x' then c.[so_luong] else 0 end)  [ton]
    from [category] a
    left join [TDK] b on a.[ma_hang]=b.[ma_hang] and a.[KHO_LUU_TRU]=b.[KHO_LUU_TRU]
    left join [NX] c on a.[ma_hang]=c.[ma_hang] and a.[KHO_LUU_TRU]=c.[KHO_LUU_TRU]
    group by a.[ma_hang], a.[KHO_LUU_TRU]
    order by a.[ma_hang], a.[KHO_LUU_TRU]

    Còn connect qua SQL của Access bạn tùy biến lại

    Cảm ơn bạn nhiều ạ, OT sẽ kiểm tra và thông tin lại ạ
    @excel_lv1.5 cảm ơn bạn đã quan tâm và giúp đỡ OT.
    Bảng 'DMHH' không có Kho bạn ạ, mà nó nằm một bảng khác nữa ví dụ là bảng 'DMKHO' bạn ạ T_T

    Chỗ trống như hình là "A" hay trống vậy em?

    Cảm ơn anh Hai Lúa đã quan tâm ạ.
    Chỗ trống đó là "A" anh ạ, xin lỗi anh OT không kéo hết lên trên.
    Vì OT sử dụng câu lệnh truy vấn để tìm theo từng mã và từng kho lên chỉ có một mã và một kho thôi ạ.

    Em thử code sau và biến tấu nhé.

    Sub GomDL_HLMT()
        Dim strSQL As String
        strSQL = "SELECT MA_HANG, SO_LUONG AS TONDAUKY, KHO_LUU_TRU, 0 AS NHAP,0 AS XUAT, SO_LUONG AS TON FROM [TDK$] " & _
                 "UNION ALL SELECT MA_HANG, 0, KHO_LUU_TRU, IIF(KIEU='N',SO_LUONG,0) AS NHAP,IIF(KIEU='X',SO_LUONG,0) AS XUAT, IIF(KIEU='N',SO_LUONG,0)-IIF(KIEU='X',SO_LUONG,0) FROM [NX$]"
        With CreateObject("ADODB.Connection")
            .Open ("Provider=Microsoft.ACE.OLEDB.12.0;Extended Properties=Excel 12.0;Data Source=" & ThisWorkbook.FullName)
            Sheet6.Range("A2").CopyFromRecordset .Execute("SELECT MA_HANG, SUM(TONDAUKY), KHO_LUU_TRU, SUM(NHAP), SUM(XUAT),SUM(TON) FROM (" & strSQL & ") GROUP BY MA_HANG, KHO_LUU_TRU")
        End With
    
    End Sub

    Cùng suy nghĩ là dùng Union Query như bác HLMT. Đây là cách truyền thống bên Access hay làm để xử lý NXT.
    🙂

    Bài toán này dùng Union là hợp lý, khó có thể dùng join. Vì sao thì đã giải thích ở bài trên.

    Xin chào excel_lv1.5
    OT test thử câu lệnh truy vấn trên của bạn và đã biết cách tham chiếu thêm đến bảng DMKHO, nhưng có một vấn đề đó là cái đoạn:
    "sum(b.) ,
    sum(case when c.='n' then c. else 0 end) ,
    sum(case when c.='x' then c. else 0 end) ,
    sum(b.)+sum(case when c.='n' then c. else 0 end)-sum(case when c.='x' then c. else 0 end) "
    OT đang thấy ra kết quả không đúng ạ.
    Ví dụ OT chỉ kiểm tra tồn đầu kỳ và so sánh với câu lệnh "sum(b.) " dữ liệu là "150" nhưng kết quả chạy câu lệnh này là "33600"
    Bạn xem giúp với ạ

    Bạn gửi cho tôi câu lệnh bạn điều chỉnh tôi xem thử!

    Dạ đây ạ, OT gửi bạn nhờ bạn kiểm tra giúp ạ.

    Select a.[MA_HANG],b.[KHO_LUU_TRU],c.[LOTNO],
    sum(b.[QTY]) [TDK],
    sum(case when c.[KIEU]='N' then c.[QTY] else 0 end) [NHAP],
    sum(case when c.[KIEU]='X' then c.[QTY] else 0 end) [XUAT],
    sum(b.[QTY])+sum(case when c.[KIEU]='N' then c.[QTY] else 0 end)-sum(case when c.[KIEU]='X' then c.[QTY] else 0 end)  [TON]
    From [KHOHANG].[dbo].[DMHH] a
    left join [KHOHANG].[dbo].[TONDAUKY] b on a.[MA_HANG]=b.[MA_HANG]
    left join [KHOHANG].[dbo].[XN] c on a.[MA_HANG]=c.[MA_HANG]
    Where (c.[MA_HANG] = N'MH0001' and c.[LOTNO] =N'L246357')
    and (b.[MA_HANG]= N'MH0001' and b.[LOTNO]=N'L246357' and b.[KHO_LUU_TRU] like N'%A%')
    Group by a.[MA_HANG],b.[KHO_LUU_TRU], c.[LOTNO]
    Order by a.[MA_HANG]

    Lưu ý dùng join trong trường hợp này sẽ không chính xác, bởi vì mỗi dòng phát sinh sẽ có 1 cái tồn đầu kỳ tương ứng với số dòng.Bạn gắn thêm và thì bảng danh mục bảng phải có 2 cột đó và điều kiện join cũng phải dùng thêm 2 điều kiện này, sài join cũng bình thường thôi

    Select a.[MA_HANG],a.[KHO_LUU_TRU],a.[LOTNO],
    sum(b.[QTY]) [TDK],
    sum(case when c.[KIEU]='N' then c.[QTY] else 0 end) [NHAP],
    sum(case when c.[KIEU]='X' then c.[QTY] else 0 end) [XUAT],
    sum(b.[QTY])+sum(case when c.[KIEU]='N' then c.[QTY] else 0 end)-sum(case when c.[KIEU]='X' then c.[QTY] else 0 end)  [TON]
    From [KHOHANG].[dbo].[DMHH] a
    left join [KHOHANG].[dbo].[TONDAUKY] b on a.[MA_HANG]=b.[MA_HANG] and a.[kho_luu_tru]=b.[kho_luu_tru] and a.[lotno]=b.[lotno]
    left join [KHOHANG].[dbo].[XN] c on a.[MA_HANG]=c.[MA_HANG]  and a.[kho_luu_tru]=c.[kho_luu_tru] and a.[lotno]=c.[lotno]
    Where (c.[MA_HANG] = N'MH0001' and c.[LOTNO] =N'L246357')
    and (b.[MA_HANG]= N'MH0001' and b.[LOTNO]=N'L246357' and b.[KHO_LUU_TRU] like N'%A%')
    Group by a.[MA_HANG],b.[KHO_LUU_TRU], c.[LOTNO]
    Order by a.[MA_HANG]

    Trong trường hợp này thì không phải là cái Table Dim Dmhh nữa mà là một Table Distinct mới tạo ra từ table TD và NX theo 3 điều kiện , và , bảng dmhh chỉ có sẽ không map được cả 3 điều kiện trên

    Cảm ơn Bạn nhiều từ nãy giờ OT vẫn loay hoay chưa ứng dụng các câu truy vấn của anh Hai Lúa và Bạn vào thử trong SQL, hihi
    Excel chạy ngon lành chắc là SQL cũng vậy thôi ạ,để OT thử thêm ạ.

    Dùng Union chắc ổn hơn đấy bạn, còn dùng cách tôi bạn phải tạo thêm một table mới thay cho bảng DMHH , vì trong trường hợp này bảng DMHH không sử dụng được nữa, nếu dùng SQL server bạn có thể thử cách pivot

    Cảm ơn Bạn nhiều,
    OT đã thử câu truy vấn của bạn thấy không ra kết quả gì ngoài các dòng tiêu đề ạ.
    left join .. b on a.=b. and a.=b. and a.=b.
    left join .. c on a.=c. and a.=c. and a.=c.
    OT thấy các chỗ tô đậm nó khó hiểu thế nào ấy ạ, vì trong bảng a(DMHH) không có trường 'kho_luu_tru' và 'lotno' bạn ạ.
    Khi OT xóa các dòng này đi thì số liệu có nhưng chênh lên nhiều như OT đã thông tin bài trước ạ.
    Để OT tìm hiểu thêm ạ, cảm ơn bạn đã cố gắng giúp đỡ OT.

    join nó giống hàm lookup trong excel cho phép tìm kiếm nhiều điều kiện
    Thì tôi nói là bạn làm NXT theo 3 yếu tố ,, nên bảng DMHH sẽ không sử dụng được do nó chỉ có , nên bạn dùng cách union thì sẽ ổn hơn.
    Còn vẫn muốn đi theo hướng đó thì bạn có thể thêm 1 đoạn tạo ra một bảng table DMHH mới như vầy, rồi chạy lại đoạn code trên (hoặc lồng vào đoạn code trên cũng được)

    select DISTINCT [ma_hang],[kho_luu_tru],[lotno]
    into #dmhh
    from
    (
        select [ma_hang],[kho_luu_tru],[lotno] from [TDK]
        union all
        select [ma_hang],[kho_luu_tru],[lotno] from [NX]
    ) a

    Dạ vâng cảm ơn bạn nhiều, OT sẽ test hết các trường hợp rồi thông tin lại ạ.

    Trong SQLServer không có hàm IIF vì vậy em phải chuyển từ IIF sang Case When nhé.

    Úi zời,, OT loay hoay từ nãy giờ ạ
    Vâng cảm ơn anh Hai Lúa ạ T_T

    Vẫn có hàm IIF nhe bạn

    Nó có từ bản 2012 nhé bạn. Những bản trước đó không có hàm này.

    Của OT vừa hỏi anh kỹ thuật anh ấy bảo SQL này là phiên bản 2016 ạ. hehe 😀

    Em hỏi kỹ lại anh IT đó coi phiên bản 2012 có chưa nhé.

    Anh ý không có chuyên môn về những câu lệnh select kiểu này đâu anh, nếu anh ấy biết OT không phải vất vả nhiều về vấn đề này rồi anh ạ.
    Anh ấy chỉ cho phép OT quyền vào rồi cũng tạo backup data cho OT nghịch trên DB sau khi backup thôi anh ạ, chứ sờ vào DB thật anh ấy mắng ạ.

    Ý anh là phiên bản 2012 về sau mới có hàm IIF. Những phiên bản trước đó chưa có hàm này. Hay ý em là khác? Ý em là em đang dùng SQLServer 2016 mà không dùng được đoạn truy vấn trên?

    À ý em là phiên bản SQL của OT là sau 2012 rồi nên OT yên tâm không phải sửa gì câu lệnh truy vấn của anh nữa ạ.
    OT chạy câu lệnh truy vấn của anh rồi và nó không báo lỗi gì có nghĩa là iff có thể sử dụng được ạ, nhưng kết quả chưa đúng chắc là sai sót chỗ nào thôi ạ do OT bổ sung thêm select kèm 1 thêm trường, OT đang mò mẫm.
    Hic cả ngày hôm nay không làm ăn gì được ngoài test cái vấn đề này anh ạ, công việc ùn hôm sau làm tiếp cũng được anh ạ hehe

  25. hands says:

    Anh Hai Lúa Miền Tây ơi câu lệnh này của anh OT thử trên Excel đúng ý mình rồi nhưng OT đang thử trong SQL và thấy anh không tham chiếu đến bảng DMHH
    Anh có thể sửa thêm cho OT tham chiếu thêm đến bảng này nữa được không ạ, vì OT muốn dựa vào Mã hàng để lấy thêm các thông tin khác từ bảng này khi cần ạ.
    Cảm ơn anh Hai Lúa Miền Tây

    ...
        strSQL = "SELECT MA_HANG, SO_LUONG AS TONDAUKY, KHO_LUU_TRU, 0 AS NHAP,0 AS XUAT, SO_LUONG AS TON FROM [TDK$] " & _
                 "UNION ALL SELECT MA_HANG, 0, KHO_LUU_TRU, IIF(KIEU='N',SO_LUONG,0) AS NHAP,IIF(KIEU='X',SO_LUONG,0) AS XUAT,
    ..

    Cùng suy nghĩ là dùng Union Query như bác HLMT. Đây là cách truyền thống bên Access hay làm để xử lý NXT.
    🙂

    Hehe, OT cảm ơn anh ongke0711 ạ

    Em nên nghiên cứu thêm về các kiểu truy vấn nhé.

    SELECT     A.MA_HANG,
               B.TEN_HANG,
               SUM(TONDAUKY),
               KHO_LUU_TRU,
               SUM(NHAP),
               SUM(XUAT),
               SUM(TON)
    FROM       (" & strSQL & ") A
    INNER JOIN [DMHH$] B
    ON         A.MA_HANG=B.MA_HANG
    GROUP BY   A.MA_HANG,
               KHO_LUU_TRU,
               B.TEN_HANG

    Xin chào anh Hai Lúa, sau một hồi loay hoay với câu lệnh truy vấn bên dưới của anh OT đã ứng dụng thử vào SQL được rồi ạ, cảm ơn anh Hai Lúa nhiều ạ.

    SELECT      NXT.[MA_HANG],NXT.[LOTNO],--MH.[TEN_HANG],
    SUM(TONDAUKY) AS [TONDAUKY],SUM(NHAP) AS [NHAP],SUM(XUAT) AS [XUAT],SUM(TON) AS [TON]
    FROM       (
    SELECT TDK.[MA_HANG], TDK.[KHO_LUU_TRU],TDK.[LOTNO], TDK.[QTY] AS [TONDAUKY], 0 AS [NHAP],0 AS [XUAT], TDK.[QTY] AS [TON]
    FROM [KHOHANG].[dbo].[TONDAUKY] TDK WHERE (TDK.[MA_HANG]= N'MH0001' AND TDK.[LOTNO]=N'L246357' AND TDK.[KHO_LUU_TRU] LIKE N'%A%')
    UNION ALL
    SELECT XN.[MA_HANG], XN.[KHO_LUU_TRU],XN.[LOTNO], 0, IIF(XN.[KIEU]='N',XN.[QTY],0) AS [NHAP],IIF(XN.[KIEU]='X',XN.[QTY],0) AS [XUAT], IIF(XN.[KIEU]='N',XN.[QTY],0)-IIF(XN.[KIEU]='X',XN.[QTY],0)
    FROM [KHOHANG].[dbo].[XN] XN WHERE (XN.[MA_HANG]= N'MH0001' AND XN.[LOTNO]=N'L246357' ) AND (XN.[NGAY_TT] >='20201001' AND  XN.[NGAY_TT] <='20201007')
    ) NXT
    --INNER JOIN [KHOHANG].[dbo].[DMHH] MH ON NXT.[MA_HANG]=MH.[MA_HANG]
    GROUP BY   NXT.[MA_HANG],NXT.[LOTNO]--,MH.[TEN_HANG]

    Tuy nhiên với câu lệnh trên OT phải xóa bỏ chuyển sang comment những chỗ
    –INNER JOIN .. MH ON NXT.=MH.
    –MH.
    Thì kết quả mới chạy đúng. híc nhìn câu lệnh mới truy vấn theo vài trường mà thấy dài thấy khiếp quá.

    Còn nếu để tham chiếu đến nó sẽ trả về kết quả sai ạ, không biết OT còn sai sót nào không anh:

    TONDAUKY    = 1050.000;
    NHAP =  2723.000;
    XUAT  =  2800.000;
    TON =    973.000
  26. hands says:

    join nó giống hàm lookup trong excel cho phép tìm kiếm nhiều điều kiện
    Thì tôi nói là bạn làm NXT theo 3 yếu tố ,, nên bảng DMHH sẽ không sử dụng được do nó chỉ có , nên bạn dùng cách union thì sẽ ổn hơn.
    Còn vẫn muốn đi theo hướng đó thì bạn có thể thêm 1 đoạn tạo ra một bảng table DMHH mới như vầy, rồi chạy lại đoạn code trên (hoặc lồng vào đoạn code trên cũng được)

    select DISTINCT [ma_hang],[kho_luu_tru],[lotno]
    into #dmhh
    from
    (
        select [ma_hang],[kho_luu_tru],[lotno] from [TDK]
        union all
        select [ma_hang],[kho_luu_tru],[lotno] from [NX]
    ) a

    Xin chào bạn excel_lv1.5,
    OT thử chạy câu lệnh này trước trước SQL hệ thống hiển thị thông báo thành thông , chạy lần thứ 2 không được như vậy có nghĩa là đã có dmhh?
    Chỗ này bạn cho hỏi OT thêm khi chạy thế này mặc dù là câ lệnh Select nhưng nó có đưa dữ liệu thêm mới vào bảng dmhh trong DB không ạ?
    Nếu nó thêm vào có lẽ là làm theo cách này không được an toàn phải không ạ.
    Và tiếp theo OT chạy lại câu #188 thì dữ liệu vẫn không xuất hiện gì ạ, hihi dù sao vấn đề của OT cũng đã được giải quyết theo cách làm của anh Hai Lúa được rồi nên bạn không cần phải bận tâm nữa đâu ạ.
    Nếu không sử dụng được các câu lệnh truy vấn kiểu SQL nữa OT cũng đã nghĩ đến việc lấy dữ liệu của từng bảng về theo các điều kiện truy vấn rồi dùng VBA xử lý ra dữ liệu NXT ạ, nhưng cách này nếu người dùng lọc khoảng thời gian dài mà bản ghi nhiều chắc là ngồi khóc :D.
    Xin cảm ơn tất cả mọi người đã giúp đỡ OT ạ.
    Kính chúc mọi người buổi tối vui khỏe.
    %$$

  27. hands says:

    Xin chào các bạn,
    Hic đang máu me hứng thú với SQL, OT đang muốn một câu lệnh truy vấn để so sánh 2 bảng dữ liệu A & B kết quả làm để trả về dữ liệu như trong vùng màu xanh.
    OT đã tìm kiếm nguồn trên trên mạng và loay hoay thử nhưng chưa tìm thấy câu lệnh nào phù hợp với vấn đề của OT đề cập nên OT up lên đây nhờ các bạn xem & giúp đỡ OT ạ.

    2542

    Chắc là

    SELECT A.MA_HANG AS MA_HANG,A.KHO_LUU_TRU AS KHO_LUU_TRU,A.TON AS TON_A,B.TON AS TON_B,A.TON-B.TON AS CHENH_LECH FROM A INNER JOIN B ON A.MA_HANG=B.MA_HANG AND A.KHO_LUU_TRU=B.KHO_LUU_TRU

    Cảm ơn bạn Hau151978 đã quan tâm và giúp đỡ OT ạ,
    OT thử câu lệnh truy vấn của bạn trong môi trường Excel thì báo lỗi: "Syntax error in JOIN operation."
    Bạn xem giúp OT với ạ:

    Sub SOSANH2BANG_SQL()
        Dim strSQL As String
        strSQL = "SELECT A.MA_HANG AS MA_HANG,A.KHO_LUU_TRU AS KHO_LUU_TRU,A.TON AS TON_A,B.TON AS TON_B,A.TON - B.TON AS CHENH_LECH FROM [A$] " & _
                 "INNER JOIN [B$] ON A.MA_HANG=B.MA_HANG AND A.KHO_LUU_TRU=B.KHO_LUU_TRU"
        With CreateObject("ADODB.Connection")
            .Open ("Provider=Microsoft.ACE.OLEDB.12.0;Extended Properties=Excel 12.0;Data Source=" & ThisWorkbook.FullName)
            Sheet9.Range("A3").CopyFromRecordset .Execute(strSQL)
        End With
    End Sub

    A! Được rồi cảm ơn bạn Hau151978 .
    Tôi giữ nguyên câu lệnh truy vấn trên của bạn:

    strSQL = "SELECT A.MA_HANG AS MA_HANG,A.KHO_LUU_TRU AS KHO_LUU_TRU,A.TON AS TON_A,B.TON AS TON_B,A.TON - B.TON AS CHENH_LECH FROM A " & _
                 "INNER JOIN B ON A.MA_HANG=B.MA_HANG AND A.KHO_LUU_TRU=B.KHO_LUU_TRU"

    Nhưng trong Excel phải thêm Name A & name B cho 2 bảng thì được, tôi chưa biết nguyên nhân vì sao lại như vậy.
    Bạn nào biết xin hãy giải thích giúp với ạ.

    Ahihi, thì ra là vậy A & B nếu không đặt tên thì Code nó không hiểu Bảng A hay cột A.
    Nếu sửa bảng A thành Bang_1 và bảng B thành Bang_2 thì không cần phải đặt name cho mỗi bảng..
    OT hiểu như vậy, ^_^
    :yahoo:

    Híc không hiểu sao Tây họ làm phức tạp thế ạ, hay do OT hiểu là so sánh không phải là nối nên tìm kiếm không đúng từ khóa ạ:
    [URL stackoverrun.com/vi/q/3642048

    Mình thì nghĩ là A$ và B$ khác A và B nên Excel không hiểu, nếu không dùng name thì có thể đặt alias thành … FROM [A$] A INNER JOIN [B$] B… (mình đoán thôi).

    Đúng rồi bạn Hau151978 khi nãy OT cũng thử lại và đúng là làm vậy thì OK ạ. :"'

  28. hands says:

    Xin chào các bạn,
    OT đang sử dụng 3 câu lệnh truy vấn sau và lấy được kết quả ra 3 vùng, như trong kèm bến dưới:

    --Truy vấn 1:
    SELECT C.[ODER_NUMBER],COUNT(C.[ODER_NUMBER]) AS [TYPE_A]
    FROM [TEST].[dbo].[THUC_TE] C
    WHERE  (C.[EPXCODE] like N'%201005%') GROUP BY C.[ODER_NUMBER]
    
    --Truy vấn 2:
    SELECT C.[ODER_NUMBER],MAX(C.[ODER_CHANGE]) as [TYPE_P]
    FROM [TEST].[dbo].[THUC_TE] C
    WHERE  (C.[EPXCODE] like N'%201005%') GROUP BY C.[ODER_NUMBER]
    
    --Truy vấn 3:
    SELECT P.[PLAN_NUMBER],P.[ITEM_CODE],MAX(P.[QTY]) AS [QTY]
    FROM [TEST].[dbo].[KE_HOACH] P
    INNER JOIN [TEST].[dbo].[THUC_TE] C ON P.[EPXCODE] =  C.[EPXCODE] AND  P.[PLAN_NUMBER] =  C.[ODER_NUMBER]
    WHERE (P.[EPXCODE] like N'%201005%')
    GROUP BY  P.[PLAN_NUMBER],P.[ITEM_CODE],P.[EPXCODE]

    Nhờ các bạn giúp đỡ để làm thế nào để gộp cả 3 câu lệnh này thành 1 để có thể ra được bảng dữ liệu gộp ạ:

    2544

    SELECT P.[PLAN_NUMBER],P.[ITEM_CODE],MAX(P.[QTY]) AS [QTY], a.[TYPE_A], a.[TYPE_P]
    FROM [TEST].[dbo].[KE_HOACH] P
    INNER JOIN [TEST].[dbo].[THUC_TE] C ON P.[EPXCODE] =  C.[EPXCODE] AND  P.[PLAN_NUMBER] =  C.[ODER_NUMBER]
    inner join
    (
        SELECT C.[ODER_NUMBER],COUNT(C.[ODER_NUMBER]) AS [TYPE_A],MAX(C.[ODER_CHANGE]) as [TYPE_P]
        FROM [TEST].[dbo].[THUC_TE] C
        WHERE  (C.[EPXCODE] like N'%201005%')
        GROUP BY C.[ODER_NUMBER]
    ) A on P.[PLAN_NUMBER]=a.[ODER_NUMBER]
    WHERE (P.[EPXCODE] like N'%201005%')
    GROUP BY  P.[PLAN_NUMBER],P.[ITEM_CODE],P.[EPXCODE]

    Lồng nó vào là được bạn

    SELECT P.[PLAN_NUMBER],P.[ITEM_CODE],MAX(P.[QTY]) AS [QTY], COUNT(C.[ODER_NUMBER]) AS [TYPE_A],MAX(C.[ODER_CHANGE]) as [TYPE_P]
    FROM [TEST].[dbo].[KE_HOACH] P
    INNER JOIN [TEST].[dbo].[THUC_TE] C ON P.[EPXCODE] =  C.[EPXCODE] AND  P.[PLAN_NUMBER] =  C.[ODER_NUMBER]
    WHERE (P.[EPXCODE] like N'%201005%')
    GROUP BY  P.[PLAN_NUMBER],P.[ITEM_CODE],P.[EPXCODE]

    Thử cách này xem nhé. Đưa 2 cái Count() vô câu truy vấn thứ 3 luôn.

    Xin chào @excel_lv1.5 , @ongke0711,
    Cảm ơn 2 người OT đã thử thành công với 2 câu lệnh gộp trên.
    Đúng là OT đã nghĩ đến cách dùng 'INNER JOIN' sau khi được @Hau151978 hướng dẫn ở bài #203 và OT cũng đã viết vào câu lệnh 3 nhưng chưa biết lồng ghép, kỹ thuật lồng ghép đúng cả một nghệ thuật.
    Xin cảm ơn tất cả mọi người.

  29. hands says:

    Xin chào tất cả các bạn,
    Vẫn là vấn đề liên quan đến câu lệnh truy vấn lấy dữ liệu NXT ạ, cụ thể là:
    OT có 1 câu truy vấn bên dưới, nếu chạy toàn bộ câu lệnh này thì gọi là CAU LENH1: BAO BAO GOM KHO_LUU_TRU
    Thì là câu lệnh 1 và trả về dữ liệu hình ảnh bên dưới của câu lệnh 1,bao gồm cột KHO_LUU_TRU:

    Còn nếu chạy khúc giữa, từ :
    …2) CAU LENH2: KHONG BAO GOM KHO_LUU_TRU
    đến
    —————————–>KET THUC CAU LENH 2
    Thì là câu lệnh 2 và trả về dữ liệu hình ảnh bên dưới của câu lệnh 2, không bao gồm cột KHO_LUU_TRU:

    Nhưng các câu truy vấn trên chỉ lấy được 1 mã hàng hoặc 1 kho lưu trữ mà không thể lấy nhiều mã hàng hay nhiều kho lưu trữ.
    Nhờ các bạn giúp đỡ OT câu lệnh để xử lý được vấn đề này với ạ.

    OT xin phép gửi câu lệnh & ảnh kèm dữ liệu mô phỏng để các bạn xem & giúp đỡ ạ:

    ---1) CAU LENH1: BAO BAO GOM KHO_LUU_TRU
    SELECT Z.[MA_HANG],T.[KHO_LUU_TRU],Z.[LOTNO],MAX(Z.TONDAUKY) AS [TONDAUKY],MAX(Z.NHAP) AS [NHAP],MAX(Z.XUAT) AS [XUAT],MAX(Z.TON) AS [TON]
    FROM (
    
    ---2) CAU LENH2: KHONG BAO GOM KHO_LUU_TRU
    SELECT NXT.[MA_HANG],NXT.[LOTNO],
    SUM(TONDAUKY) AS [TONDAUKY],SUM(NHAP) AS [NHAP],SUM(XUAT) AS [XUAT],SUM(TON) AS [TON]
    FROM (
    SELECT TDK.[MA_HANG],TDK.[KHO_LUU_TRU],TDK.[LOTNO], TDK.[SO_LUONG] AS [TONDAUKY], 0 AS [NHAP],0 AS [XUAT], TDK.[SO_LUONG] AS [TON]
    FROM [KHOHANG].[dbo].[KQ_KIEM_KE] TDK
    WHERE TDK.[NGAY_KIEM_KE] = (SELECT MAX([NGAY_KIEM_KE]) FROM [KHOHANG].[dbo].[KQ_KIEM_KE])
    AND (TDK.[MA_HANG]= N'MH0001') AND (TDK.[KHO_LUU_TRU] LIKE N'%A%')
    AND (TDK.[LOTNO]=N'L246357')
    UNION ALL
    SELECT XN.[MA_HANG],XN.[KHO_LUU_TRU],XN.[LOTNO], 0 AS [TONDAUKY],
    IIF((XN.[KIEU]=1 And XN.[KHO_LUU_TRU] LIKE N'%A%') or (XN.[KIEU]=3 And XN.[KHO_XUAT_HANG] LIKE N'%A%') ,XN.[SO_LUONG],0) AS [NHAP],
    IIF((XN.[KIEU]>=2 And XN.[KHO_LUU_TRU] LIKE N'%A%'),XN.[SO_LUONG],0) AS [XUAT],
    IIF((XN.[KIEU]=1 And XN.[KHO_LUU_TRU] LIKE N'%A%') or (XN.[KIEU]=3 And XN.[KHO_XUAT_HANG] LIKE N'%A%'),XN.[SO_LUONG],0)-IIF((XN.[KIEU]>=2 And XN.[KHO_LUU_TRU] LIKE N'%A%'),XN.[SO_LUONG],0 ) AS [TON]
    FROM [KHOHANG].[dbo].[NHAP_XUAT] XN
    WHERE LEFT(XN.[NGAY_THANG],6) >= (SELECT MAX([NGAY_KIEM_KE]) FROM [KHOHANG].[dbo].[KQ_KIEM_KE])
    -----------------------------KET THUC CAU LENH 2
    
    ) Z
    INNER JOIN [KHOHANG].[dbo].[KQ_KIEM_KE] T ON  Z.MA_HANG=T.MA_HANG
    GROUP BY   Z.[MA_HANG],T.[KHO_LUU_TRU],Z.[LOTNO]
    -----------------------------KET THUC CAU LENH 1

    2545
    OT đã nghĩ đến việc bỏ các câu lệnh WHERE cho = N'MH0001') và LIKE N'%A%'
    Nhưng vấn đề ở chỗ trong câu lệnh IIF bên dưới, nếu có 3 KHO_LUU_TRU ( hoặc KHO_XUAT_HANG) thì có thể liệt kê thêm được, tuy nhiên nếu có rất nhiều KHO_LUU_TRU thì sẽ xử lý thế nào, mong các bạn xem & giúp đỡ OT với ạ:

    ...
    IIF((XN.[KIEU]=1 And XN.[KHO_LUU_TRU] LIKE N'%A%') or (XN.[KIEU]=3 And XN.[KHO_XUAT_HANG] LIKE N'%A%') ,XN.[SO_LUONG],0) AS [NHAP],
    IIF((XN.[KIEU]>=2 And XN.[KHO_LUU_TRU] LIKE N'%A%'),XN.[SO_LUONG],0) AS [XUAT],
    IIF((XN.[KIEU]=1 And XN.[KHO_LUU_TRU] LIKE N'%A%') or (XN.[KIEU]=3 And XN.[KHO_XUAT_HANG] LIKE N'%A%'),XN.[SO_LUONG],0)-IIF((XN.[KIEU]>=2 And XN.[KHO_LUU_TRU]
    ...

    Ví dụ thông tin cần thêm (khi cần) ạ:
    Bảng danh mục hàng hóa: tên bảng có 2 trường ,
    Bảng danh mục Kho hàng: tên bảng có 2 trường ,

    SELECT NXT.[MA_HANG],NXT.[KHO_LUU_TRU],NXT.[LOTNO],
    SUM(TONDAUKY)  [TONDAUKY],SUM(NHAP)  [NHAP],SUM(XUAT)  [XUAT],SUM(TON)  [TON]
    FROM
    (
        SELECT TDK.[MA_HANG],TDK.[KHO_LUU_TRU],TDK.[LOTNO], TDK.[SO_LUONG]  [TONDAUKY], 0  [NHAP],0  [XUAT], TDK.[SO_LUONG]  [TON]
        FROM [KHOHANG].[dbo].[KQ_KIEM_KE] TDK
        WHERE TDK.[NGAY_KIEM_KE] = (SELECT MAX([NGAY_KIEM_KE]) FROM [KHOHANG].[dbo].[KQ_KIEM_KE])
        AND TDK.[KHO_LUU_TRU] LIKE N'%A%'
        UNION ALL
        SELECT XN.[MA_HANG],XN.[KHO_LUU_TRU],XN.[LOTNO], 0 AS [TONDAUKY],
        (case when XN.[KIEU] in (1,3)  And XN.[KHO_LUU_TRU] LIKE N'%A%' then XN.[SO_LUONG] else 0 end)  [NHAP],
        (case when XN.[KIEU] =2 And XN.[KHO_LUU_TRU] LIKE N'%A%'then XN.[SO_LUONG] else 0 end)  [XUAT],
        (case when XN.[KIEU] in (1,3)  And XN.[KHO_LUU_TRU] LIKE N'%A%' then XN.[SO_LUONG] else 0 end)-(case when XN.[KIEU] =2 And XN.[KHO_LUU_TRU] LIKE N'%A%'then XN.[SO_LUONG] else 0 end) [TON]
        FROM [KHOHANG].[dbo].[NHAP_XUAT] XN
        WHERE LEFT(XN.[NGAY_THANG],6) >= (SELECT MAX([NGAY_KIEM_KE]) FROM [KHOHANG].[dbo].[KQ_KIEM_KE]
    ) A
    group by NXT.[MA_HANG],[NXT.KHO_LUU_TRU],NXT.[LOTNO]

    Tôi chỉnh lại như vầy

    Câu truy vấn nhìn gọn và đẹp mắt dễ nhìn quá ạ, OT cảm bạn nhiều nhiều ạ.
    Bạn cho OT hỏi thêm
    các mệnh đề Case.. When.. OT thấy có liên quan đến cụ thể một kho có ký tự A, vậy đối với các kho B,C.. và rất nhiều kho khác nữa thì xử lý thế nào ạ? OT cũng đã loay hoay suốt chỗ đó mà chưa biết cách xử lý ạ.

    Tôi thấy bạn sử dụng LIKE, nếu có nhiều nhóm như vậy bạn tạo thêm cột nhóm kho rồi dùng IN
    2546
    Giả sử bạn cần lấy các kho thuộc nhóm A,C thì bạn chỉnh điều kiện SQL như vầy, việc thêm cột có thể dùng Sub để tạo mà không cần thay đổi Table gốc

    (case when XN.[KIEU] in (1,3) And XN.[KHO_LUU_TRU] in (select [kho] from [dmkho] where nhomkho in ('A','C')) then XN.[SO_LUONG] else 0 end) [NHAP]

    cảm ơn excel_lv1.5
    Như đã nêu trên cách này với OT là gần như không thể, nghĩa là thiết kế của DB như thế nào thì không được thay đổi ạ.
    Với lại không thể chia làm nhiều nhóm kho như vậy được ạ, mỗi kho đều là độc lập ạ, xin lỗi vì OT quen dùng like nên viết vậy ạ chứ đáng ra phải viết rõ ràng là =N'Kho_A' ạ. Mỗi kho đểu có thể có 2 nghiệp vụ nhập và xuất.
    Tùy vào trường kiểu 1,2,3 để xác định giá trị là xuất hay nhập ạ.
    đoạn case when này OT vẫn đang nghĩ việc liên kết đến bảng danh mục kho như đã nêu ở bài #214 không biết có gì sai lệch không ạ.
    hic hic

    Nếu tài khoản của bạn của bạn chỉ được quyền Read thì việc tạo Table sẽ không được, nhưng Table tạm vẫn được, khi bạn không sử dụng SQL nữa Table đó sẽ tự xóa, tôi chỉnh lại code trên bằng việc sử dụng Table tạm

    SELECT TDK.[MA_HANG],TDK.[KHO_LUU_TRU],TDK.[LOTNO], TDK.[SO_LUONG]  [TONDAUKY], 0  [NHAP],0  [XUAT], TDK.[SO_LUONG]  [TON]
    into #A
    FROM [KHOHANG].[dbo].[KQ_KIEM_KE] TDK
    WHERE TDK.[NGAY_KIEM_KE] = (SELECT MAX([NGAY_KIEM_KE]) FROM [KHOHANG].[dbo].[KQ_KIEM_KE])
    AND TDK.[KHO_LUU_TRU] LIKE N'%A%'
    UNION ALL
    SELECT XN.[MA_HANG],XN.[KHO_LUU_TRU],XN.[LOTNO], 0 AS [TONDAUKY],
    (case when XN.[KIEU] in (1,3)  And XN.[KHO_LUU_TRU] LIKE N'%A%' then XN.[SO_LUONG] else 0 end)  [NHAP],
    (case when XN.[KIEU] =2 And XN.[KHO_LUU_TRU] LIKE N'%A%'then XN.[SO_LUONG] else 0 end)  [XUAT],
    (case when XN.[KIEU] in (1,3)  And XN.[KHO_LUU_TRU] LIKE N'%A%' then XN.[SO_LUONG] else 0 end)-(case when XN.[KIEU] =2 And XN.[KHO_LUU_TRU] LIKE N'%A%'then XN.[SO_LUONG] else 0 end) [TON]
    FROM [KHOHANG].[dbo].[NHAP_XUAT] XN
    WHERE LEFT(XN.[NGAY_THANG],6) >= (SELECT MAX([NGAY_KIEM_KE]) FROM [KHOHANG].[dbo].[KQ_KIEM_KE]
    
    SELECT NXT.[MA_HANG],NXT.[KHO_LUU_TRU],NXT.[LOTNO],
    SUM(TONDAUKY)  [TONDAUKY],SUM(NHAP)  [NHAP],SUM(XUAT)  [XUAT],SUM(TON)  [TON]
    FROM #A
    group by NXT.[MA_HANG],[NXT.KHO_LUU_TRU],NXT.[LOTNO]

    Việc sử dụng Subquery trong các môi trường khác không biết sao chứ trong SQL server bạn cứ sử dụng bình thường, trừ khi một sub được gọi nhiều hơn một lần thì nên lưu sub đó vào bảng tạm để tranh query nhiều lần

    Thì tùy biến thôi bạn, nếu không thay đổi Table gốc thì dùng sub tạo thêm thôi, đây là ví dụ cho bài #218, bạn tùy biến cho trường hợp của bạn

    (case when XN.[KIEU] in (1,3) And XN.[KHO_LUU_TRU] in
    (
        select [kho] from
            (
                select *, left([kho],1) [nhomKho] from [dmkho]
            ) where [nhomkho] in ('A','C')
    ) then XN.[SO_LUONG] else 0 end) [NHAP]

    Dạ, vâng cảm ơn bạn đã chỉ thêm cho OT biết về cách dùng bảng tạm ạ.
    OT cứ tưởng tạo cái gì đó thì dùng lệnh create… thì mới đáng lo, còn lệnh trên thì selec có lẽ chắc là không vấn đề gì ạ.

    Anh nghĩ em không nên quá lạm dụng dùng Subquerry. Có những bài toán dùng Subquery là tuyệt chiêu nhưng có những bài nó sẽ chạy ì ạch . Đối với CSDL lớn chục ngàn dòng, nhiều cột thì dùng nhiều cái subquery lồng nhau, sub của sub như bài của em sẽ không là cách hay. Thường thì nên tạo thêm table tạm để xử lý để khỏi phải lồng ghép nhiều query.

    Xin chào anh ongke0711,
    Cảm ơn anh đã giúp đỡ góp ý cho OT ạ.
    Đây là DB của nhà cung cấp phần mềm thiết kế nên có lẽ không thể hay là không nên chỉnh sửa vào đó của họ được đâu anh ạ.
    Sờ mó vào đó đến lúc có vde gì chắc là họ không chịu đâu anh.. Hihi
    OT đã từng nghĩ đến việc lấy đữ liệu của từng bảng về Excel rồi dùng vba xử lý nhưng dữ liệu ở bảng Nhập Xuất hàng ngày quá nhiều bản ghi, nếu lấy trong vòng 1 tháng khả năng Excel không đủ sức chứa nó ạ.
    Thế nên có lẽ với OT không còn cách lựa chọn nào nữa anh ạ, T_T

  30. hands says:

    Xin chào các bạn,
    OT xin gửi dữ liệu bài trên để nhờ các bạn xem và giúp đỡ ạ:

    http://www.mediafire.com/file/h1ekzi5bfky9dpk/DATA_NXT.xlsx/file

    2547

    Em vào bài sau của anh @Nguyễn Duy Tuân sẽ có câu lệnh XNT em cần nhé.
    http://www.giaiphapexcel.com/diendan/threads/thi-t%E1%BA%A1o-s%E1%BB%95-th-nxt-v%E1%BB%9Bi-t%E1%BB%91c-%C4%91%E1%BB%99-nhanh-nh%E1%BA%A5t-d%E1%BB%AF-li%E1%BB%87u-65-532-d%C3%B2ng.89723/

    Thế ạ anh, vâng để OT coi bên đó ạ.
    Cảm ơn Hai Lúa đã chỉ dẫn

Leave a Reply

Your email address will not be published. Required fields are marked *

Quảng cáo

Cũ vẫn chất

Xem thêm