Dynamics 365 Business Central: How to find duplicates via AL (Filter for duplicate values)

Dynamics 365 Business Central

Hi, Readers.
I was asked an interesting question on LinkedIn yesterday, is there a way to find out duplicates via AL? So in this post I would like to briefly talk about this topic.

First of all, as test data, I use the copy feature on the Item list page to create multiple items with the same Description, and we need to filter for these lines.

Because the test results are the same, so I put the test video first in front of this post.

I will mainly introduce two methods, let’s start.

1. Using Record.Count() Method to check if any of the the filtered records have more than 1 entry, those would be the duplicate values.

We have used this method briefly in How to check whether selected records have the same value in a field? This time it is similar to it, but it needs to collect all the filter conditions, i.e. duplicate Descriptions.

Source Code: Github

pageextension 50144 ItemListExt extends "Item List"
{
    actions
    {
        addfirst(processing)
        {
            action(FindDuplicates)
            {
                Caption = 'Find Duplicates';
                ApplicationArea = All;
                Promoted = true;
                PromotedCategory = Process;
                PromotedIsBig = true;
                Image = FilterLines;

                trigger OnAction()
                var
                    Item01: Record Item;
                    Item02: Record Item;
                    DuplicatesFilter: Text[2048];
                    ItemDescription: Text[100];
                begin
                    DuplicatesFilter := '';
                    ItemDescription := '';
                    Item01.Reset();
                    Item01.SetCurrentKey(Description);
                    Item01.Ascending(true);
                    if Item01.FindSet() then
                        repeat
                            if ItemDescription <> Item01.Description then begin
                                Item02.Reset();
                                Item02.SetRange(Description, Item01.Description);
                                if Item02.Count > 1 then begin
                                    ItemDescription := Item01.Description;
                                    if DuplicatesFilter = '' then
                                        DuplicatesFilter := Item01.Description
                                    else
                                        DuplicatesFilter := DuplicatesFilter + '|' + Item01.Description;
                                end;
                            end;
                        until Item01.Next() = 0;
                    //Message(DuplicatesFilter);
                    Rec.SetFilter(Description, DuplicatesFilter);
                    CurrPage.Update();
                end;
            }
        }
    }
}

I believe you all know the basic method, now let me improve it into a slightly more general method.

local procedure FindDuplicates: Using RecordRef Data Type and FieldRef Data Type, you only need Table ID and Field ID to get the filtered text.

Action Code:

Source Code: Github

pageextension 50144 ItemListExt extends "Item List"
{
    actions
    {
        addfirst(processing)
        {
            action(FindDuplicates)
            {
                Caption = 'Find Duplicates';
                ApplicationArea = All;
                Promoted = true;
                PromotedCategory = Process;
                PromotedIsBig = true;
                Image = FilterLines;

                trigger OnAction()
                var
                    DuplicatesFilter: Text[2048];
                begin
                    DuplicatesFilter := FindDuplicates(Database::Item, 3);
                    //Message(DuplicatesFilter);
                    Rec.SetFilter(Description, DuplicatesFilter);
                    CurrPage.Update();
                end;
            }
        }
    }

    local procedure FindDuplicates(TableID: Integer; FieldID: Integer) DuplicatesFilter: Text[2048]
    var
        RecRef: RecordRef;
        FldRef: FieldRef;
        RecRef2: RecordRef;
        FldRef2: FieldRef;
        FilterText: Text[100];
    begin
        Clear(RecRef);
        Clear(FldRef);
        DuplicatesFilter := '';
        FilterText := '';
        RecRef.Open(TableID);
        FldRef := RecRef.Field(FieldID);
        RecRef.SetView(StrSubstNo('SORTING(%1)', FldRef.Caption));
        RecRef.Ascending(true);
        if RecRef.FindSet() then
            repeat
                if FilterText <> Format(FldRef.Value) then begin
                    RecRef2.Open(TableID);
                    FldRef2 := RecRef2.Field(FieldID);
                    FldRef2.SetFilter(FldRef.Value);
                    if RecRef2.FindSet() then
                        if RecRef2.Count > 1 then begin
                            FilterText := Format(FldRef2.Value);
                            if DuplicatesFilter = '' then
                                DuplicatesFilter := Format(FldRef2.Value)
                            else
                                DuplicatesFilter := DuplicatesFilter + '|' + Format(FldRef2.Value);
                        end;
                    RecRef2.Close();
                end;
            until RecRef.Next() = 0;
    end;
}

2. Using Query object to return duplicates

In fact, the practice is similar to that in the SQL statement (GROUP BY -> COUNT).

Query:

Open Query directly in the BC:

Then read the content of this Query in the code and collect the Descriptions.

Source Code: Github

pageextension 50144 ItemListExt extends "Item List"
{
    actions
    {
        addfirst(processing)
        {
            action(FindDuplicates)
            {
                Caption = 'Find Duplicates';
                ApplicationArea = All;
                Promoted = true;
                PromotedCategory = Process;
                PromotedIsBig = true;
                Image = FilterLines;

                trigger OnAction()
                var
                    DuplicatesFilter: Text[2048];
                    ItemDuplicates: Query ItemDuplicates;
                begin
                    DuplicatesFilter := '';
                    ItemDuplicates.SetFilter(No_, '>1');
                    if ItemDuplicates.Open() then
                        while ItemDuplicates.Read() do begin
                            if DuplicatesFilter = '' then
                                DuplicatesFilter := ItemDuplicates.Description
                            else
                                DuplicatesFilter := DuplicatesFilter + '|' + ItemDuplicates.Description;
                        end;
                    ItemDuplicates.Close();
                    Rec.SetFilter(Description, DuplicatesFilter);
                    CurrPage.Update();
                end;
            }
        }
    }
}

query 50112 ItemDuplicates
{
    QueryType = Normal;
    OrderBy = ascending(Description);

    elements
    {
        dataitem(Item; Item)
        {
            column(Description; Description)
            {

            }
            column(No_)
            {
                Method = Count;
            }
        }
    }
}

PS: If you want to find unique values, you need to modify the collection method of the filtered text. It should be “<>ATHENS Desk&<>PARIS Guest Chair, black”.

The above test code just extracts and saves the filter conditions. You can also use a temporary table to insert duplicate values and display them on the page or do other processing.

And please note that the source code is for reference only, you can improve it according to your own needs.

Update 2024.02.19: Dynamics 365 Business Central: How to delete Duplicate Lines (records) – Customization

END

Hope this will help.

Thanks for reading.

ZHU

コメント

Copied title and URL