Take the 2-minute tour ×
Geographic Information Systems Stack Exchange is a question and answer site for cartographers, geographers and GIS professionals. It's 100% free, no registration required.

I have an Esri file geodatabase with attribute domains defined. I need to delete some of the attribute domains but can't because "The domain is used by an attribute rule.". How might I discover which feature class(es) are using the domains?

Executing: DeleteDomain R:\v5\YT_Canvec.gdb Permanency
Start Time: Thu May 19 11:01:02 2011
ERROR 999999: Error executing function.
The domain is used by an attribute rule.
Failed to execute (DeleteDomain).
Failed at Thu May 19 11:01:02 2011 (Elapsed Time: 0.00 seconds)

There are over a hundred feature classes in the geodatabase, interactively looking at the FC field properties for each one is a non-starter. The gdb is too large to convert to a personal gdb and go in the back door with ms-access (a dodgy method anyway).


(2011-May-26): Another way to phrase this is "which Feature Class is using domain X?"

share|improve this question
    
Are you using subtyped domains? –  Kirk Kuykendall May 19 '11 at 19:43
    
@kirk, yes there is a subtype, but the domains I'm trying to remove are not using the subtype –  matt wilkie May 19 '11 at 21:49
    
In that case I think Brian's code would work. –  Kirk Kuykendall May 19 '11 at 21:52
    
@kirk, correction: I didn't think I was using subtypes+domains, but after much mucking about and opening a tech support case, it turns out I actually was using one after all. It was veritable click-fest to identify the particular remaining cuplrit. I should have invested more time in following up on your c# method! –  matt wilkie May 26 '11 at 22:51

3 Answers 3

Python has methods for listing feature classes in a geodatabase, looping through each feature class in the list, listing fields in each feature class, and showing the domain of each field.

import arcpy

#Set workspace environment to geodatabase
arcpy.env.workspace = your_gdb

#Get list of feature classes in geodatabase
FCs = arcpy.ListFeatureClasses()

#Loop through feature classes in list
for FC in FCs:

    #List fields in feature class
    fields = arcpy.ListFields(FC)

    #Loop through fields
    for field in fields:

        #Check if field has domain
        if field.domain != "":

            #Print feature class, field, domain name
            print FC, field.name, field.domain

The above code should work in ArcGIS 10 and it will print a list right in the python interpreter window. You can then copy and paste the list into a text editor or Excel to review the results more easily.

share|improve this answer
    
Will this handle subtyped domains too? –  Kirk Kuykendall May 19 '11 at 19:43
    
I'm not sure if this will handle subtypes or subtyped domains. I have never used subtypes before. If there is a domain assigned to a particular field, the domain name will be printed. –  Brian May 19 '11 at 20:10
    
beautiful, thanks Brian. Initially it didn't work for me, but eventually I remembered that listFC doesn't recurse into FeatureDatasets without some extra help (gis.stackexchange.com/questions/5893/…). All good now! :) –  matt wilkie May 19 '11 at 21:51
    
@Kirk, no it doesn't see subtypes using domains. –  matt wilkie May 26 '11 at 22:48
up vote 2 down vote accepted

Unfortunately Brian's answer, which is a direct and usable answer the question asked, does not solve my actual problem. I presume because of a bug in the gdb at hand (even though none of the feature classes have domains attached, there is still one I'm not allowed to delete). In any case I found another method for determining which fc's have associated domains. It's interactive, but much faster than going through each field property on every single fc:

Drag and drop bunches of fc's from problem gdb to another gdb and inspect the Data Transfer dialog. Linked attribute domains, if any, will be at the bottom of the list. Repeat in smaller and smaller bunches until you narrow down which @$%## fc is giving you a hard time.

finally narrowed down to 2 FCs linked to a CV domain

share|improve this answer
    
Curiously, even though drag-n-drop says HD_148009_2 is linked to the CV Domain Permanency, Brian's arcpy script reports no linked domain, and neither does the Feature Class Properties fields inspector in ArcCatalog. However now I've finally narrowed it down enough to log a bug report with Esri tech support. –  matt wilkie May 19 '11 at 22:59

Since I don't think python handles subtypes, I'm posting this c# code that should. I tested it with Esri's sample water/wastewater geodb and found the following unused domains:

HistoryType is not used
PLSSFirstDivisionType is not used
PLSSDirection is not used
PLSSPrincipalMeridian is not used
ParcelType is not used
PLSSSpecialSurveyType is not used
CartoLineType is not used
PLSSSecondDivisionType is not used

Often DBA's get annoyed that domains - which are essentially lookup tables - cannot be accessed via SQL.

This code tested from arcmap (updated per Matt's comment):

protected override void OnClick()
{
    string fgdbPath = @"C:\projects\NetTools\InfrastructureEditingTemplate\MapsandGeodatabase\LocalGovernment.gdb";
    var dict = SummarizeDomains(fgdbPath);
    ListDomains(dict);
    // list what featureclasses use a particular domain ...
    string domName = "State_Bnd_Rules";
    if (dict.ContainsKey(domName))
    {
        if (dict[domName].Count > 0)
        {
            Debug.Print("{0} is used by these featureclasses: ", domName);
            foreach (string fcfldName in dict[domName])
            {
                Debug.Print("\t{0}", fcfldName);
            }
        }
        else
            Debug.Print("{0} is not used by any featureclasses", domName);
    }
    else
    {
        Debug.Print("Domain name not found in geodb: {0}", domName);
    }
}

private void ListDomains(Dictionary<string,List<string>> dict)
{
    foreach (KeyValuePair<string, List<string>> kvp in dict)
    {
        Debug.Print("Domain {0}",kvp.Key);
        if (kvp.Value.Count > 0)
        {
            foreach (string fcfldName in kvp.Value)
            {
                Debug.Print("\t{0}", fcfldName);
            }
        }
        else
            Debug.Print("\tUNUSED DOMAIN!");
    }
}

private Dictionary<string, List<string>> SummarizeDomains(string fgdPath)
{
    var ws = Open(fgdPath);
    var dict = InitDict(ws);

    var enumDs1 = ws.get_Datasets(esriDatasetType.esriDTAny);
    IDataset ds;
    while ((ds = enumDs1.Next()) != null)
    {
        Debug.Print("processing {0}", ds.Name);
        if (ds is IObjectClass)
            LoadDomains((IObjectClass)ds, dict);
        else if (ds is IFeatureDataset)
        {
            var enumDs2 = ds.Subsets;
            enumDs2.Reset();
            IDataset ds2;
            while ((ds2 = enumDs2.Next()) != null)
            {
                if (ds2 is IObjectClass)
                    LoadDomains((IObjectClass)ds2, dict);
            }
        }
    }
    return dict;
}
private void LoadDomains(IObjectClass oc, Dictionary<string, List<string>> dict)
{
    if (oc is ISubtypes && ((ISubtypes)oc).HasSubtype)
        LoadSubtypeDomains(oc, dict);
    else
    {
        for (int i = 0; i < oc.Fields.FieldCount; i++)
        {
            var fld = oc.Fields.get_Field(i);
            if (fld.Domain == null)
                continue;
            if (dict.ContainsKey(fld.Domain.Name))
                dict[fld.Domain.Name].Add(String.Format("{0}.{1}",((IDataset)oc).Name,fld.Name));
            else
                throw new Exception("domain not found: " + fld.Domain.Name);
        }
    }
}
private void LoadSubtypeDomains(IObjectClass oc, Dictionary<string, List<string>> dict)
{
    ISubtypes subTypes = oc as ISubtypes;
    var enumSubtypes = subTypes.Subtypes;
    enumSubtypes.Reset();
    int code;
    string stName;
    while ((stName = enumSubtypes.Next(out code)) != null)
    {
        for (int i = 0; i < oc.Fields.FieldCount; i++)
        {
            string fldName = oc.Fields.get_Field(i).Name;
            var domain = subTypes.get_Domain(code, fldName);
            if (domain != null)
            {
                if (dict.ContainsKey(domain.Name))
                    dict[domain.Name].Add(String.Format("{0}.{1}.{2}",stName,((IDataset)oc).Name,fldName));
                else
                    throw new Exception("domain not found: " + domain.Name);
            }
        }
    }
}
private Dictionary<string, List<string>> InitDict(IWorkspace ws)
{
    var dict = new Dictionary<string, List<string>>(StringComparer.InvariantCultureIgnoreCase);
    var enumDomain = ((IWorkspaceDomains)ws).Domains;
    enumDomain.Reset();
    IDomain d = null;
    while ((d = enumDomain.Next()) != null)
        dict.Add(d.Name, new List<string>());
    return dict;
}

private IWorkspace Open(string fgdbPath)
{
    Type t = Type.GetTypeFromProgID("esriDataSourcesGDB.FileGDBWorkspaceFactory");
    var wsf = Activator.CreateInstance(t) as IWorkspaceFactory;
    return wsf.OpenFromFile(fgdbPath, 0);
}
share|improve this answer
    
while listing unused domains is useful, this is the inverse of the problem to be solved. I was actually looking for "which FC is using domain X?" (so I may remove the link, and make the domain an unused domain). ((I still haven't tried the code, I'm just going on the name of the function)) –  matt wilkie May 26 '11 at 22:55
    
@matt oh, yeah, that make sense. I've changed the code to show how to do that. –  Kirk Kuykendall May 27 '11 at 13:52
    
uhh, maybe this should be a full fledged question, but, where do I put this code? I can't locate the v10 equivalent of the VBA editor (Tools->Macros->Visual Basic Editor). –  matt wilkie May 31 '11 at 20:51
    
You'll need to install Visual Studio Express (free) or greater, and the ArcGIS SDK. Once you've done that, you should be able to follow this walkthrough for creating a command button, then copy and paste my code into the Click event. You'll also need to add appropriate references to the project. –  Kirk Kuykendall May 31 '11 at 21:26

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.