SQL IN equivalent in CAML
Asked Answered
E

4

12

Is there a "nice" way to create a CAML query for SharePoint that does something like this?

SELECT *
FROM table
WHERE Id IN (3, 12, ...)

Or am I stuck with a nightmare of nested <Or> nodes?


EDIT: This was my solution to generate the <Or> nodes.

/// Simulates a SQL 'Where In' clause in CAML
/// </summary>
/// <param name="columnType">Specifies the data type for the value contained by the field.</param>
/// <returns>Nested 'Or' elements portion of CAML query</returns>
public static string CamlIn<T>(string internalFieldName, string columnType, T[] values)
{
    XDocument doc = new XDocument();
    XElement prev = null;
    int index = 0;

    while (index < values.Length)
    {
        XElement element =
            new XElement("Or",
                new XElement("Eq",
                    new XElement("FieldRef",
                    new XAttribute("Name", internalFieldName)),
                new XElement("Value",
                    new XAttribute("Type", columnType),
                    values[index++].ToString())));

        if (index == values.Length - 1)
        {
            element.AddFirst(
                new XElement("Eq",
                    new XElement("FieldRef",
                    new XAttribute("Name", internalFieldName)),
                new XElement("Value",
                    new XAttribute("Type", columnType),
                    values[index++].ToString())));
        }

        if (prev != null)
            prev.AddFirst(element);
        else
            doc.Add(element);

        prev = element;
    }

    return doc.ToString(SaveOptions.DisableFormatting);
}

Usage:

int[] ids = new int[] { 1, 2, 4, 5 };
string query = string.Format("<Where>{0}</Where>", CamlIn("SomeColumn", "Number", ids));

Output:

<Where>
    <Or>
        <Or>
            <Or>
                <Eq>
                    <FieldRef Name=\"SomeColumn\" />
                    <Value Type=\"Number\">5</Value>
                </Eq>
                <Eq>
                    <FieldRef Name=\"SomeColumn\" />
                    <Value Type=\"Number\">4</Value>
                </Eq>
            </Or>
            <Eq>
                <FieldRef Name=\"SomeColumn\" />
                <Value Type=\"Number\">2</Value>
            </Eq>
        </Or>
        <Eq>
            <FieldRef Name=\"SomeColumn\" />
            <Value Type=\"Number\">1</Value>
        </Eq>
    </Or>
</Where>

Also made this overload for working with Lookup Fields a bit easier

/// <summary>
/// Simulates a SQL 'Where In' clause in CAML
/// </summary>
/// <param name="lookupId">Specify whether to use the Lookup column's Id or Value.</param>
/// <returns>Nested 'Or' elements portion of CAML query</returns>
public static string CamlIn<T>(string internalFieldName, bool lookupId, T[] values)
{
    XDocument doc = new XDocument();
    XElement prev = null;
    int index = 0;

    while (index < values.Length)
    {
        XElement element =
            new XElement("Or",
                new XElement("Eq",
                    new XElement("FieldRef",
                        new XAttribute("Name", internalFieldName),
                        lookupId ? new XAttribute("LookupId", "TRUE") : null),
                    new XElement("Value",
                        new XAttribute("Type", "Lookup"),
                        values[index++].ToString())));

        if (index == values.Length - 1)
        {
            element.AddFirst(
                new XElement("Eq",
                    new XElement("FieldRef",
                        new XAttribute("Name", internalFieldName),
                        lookupId ? new XAttribute("LookupId", "TRUE") : null),
                    new XElement("Value",
                        new XAttribute("Type", "Lookup"),
                        values[index++].ToString())));
        }

        if (prev != null)
            prev.AddFirst(element);
        else
            doc.Add(element);

        prev = element;
    }

    if (values.Length == 1)
    {
        XElement newRoot = doc.Descendants("Eq").Single();
        doc.RemoveNodes();
        doc.Add(newRoot);
    }

    return doc.ToString(SaveOptions.DisableFormatting);
}
Excruciating answered 11/12, 2009 at 22:33 Comment(0)
Z
5

NO, you'll need to deal with nested OR tags; these are supported query instructions on CAML

Maybe CAML.NET can help you in your quest.

Zapateado answered 12/12, 2009 at 0:40 Comment(0)
R
23

For those using Sharepoint 2010, there is an IN element available:

http://msdn.microsoft.com/en-us/library/ff625761.aspx

Here's a working example:

SPQuery locationsQuery = new SPQuery();
locationsQuery.Query = string.Concat("<Where>",
                                       "<In>",
                                         "<FieldRef Name='ID' />",
                                           "<Values>",
                                             "<Value Type='Number'>6</Value>",
                                             "<Value Type='Number'>7</Value>",
                                             "<Value Type='Number'>8</Value>",
                                           "</Values>",
                                       "</In>",
                                     "</Where>");
Residue answered 19/12, 2010 at 19:18 Comment(0)
R
5

FullTextSqlQuery

It is possible to search MOSS using SQL statements, using the FullTextSqlQuery class. I have no experience of using this class personally. These articles may be of use:

YACAMLQT

Alternatively, there is also YACAMLQT (Yet Another CAML Query Tool) which allows you to create SharePoint CAML queries using a T-SQL syntax.

LINQ to SharePoint

If you are up to speed with LINQ, then the LINQ to SharePoint project provides a tool to query SharePoint lists using the LINQ syntax. Please note, this tool is still in the alpha testing phase, so it may not be production ready.

U2U CAML Query Builder

If you are working with CAML queries, I would recommend using the U2U CAML Query Builder for SharePoint (2003 and 2007) tool to build up your CAML queries. The tool allows you to build up your query string, and to execute it against the target list, using a point-and-click interface, as shown below.

U2U CAML Query Builder for SharePoint in action
(source: u2u.net)

Of the above four methods, I can recommend the U2U CAML Query Builder, having used it almost daily over the last 6 months. It also appears to be the most widely used CAML tool in the SharePoint community .

Note, if you are building the CAML queries in code, then I recommend that you take a look at the CAML.NET project on CodePlex, which provides "a set of .NET language-based tools for creating dynamic, reusable CAML query components".

Ritter answered 12/12, 2009 at 0:22 Comment(1)
Didn't know about CAML.NET. May need to use that in the future. I did something similar with XElements to generate the necessary <Or>s.Excruciating
Z
5

NO, you'll need to deal with nested OR tags; these are supported query instructions on CAML

Maybe CAML.NET can help you in your quest.

Zapateado answered 12/12, 2009 at 0:40 Comment(0)
J
0

I faced a similar thing and ultimately had to create a recursive algorithm to generate the nested OR structure. Here is my algorithm

var DynamicQuery = '<Query><Where>{{DYNAMICSTRING}}</Where></Query>';
var OneOR = '<Or><Eq><FieldRef Name="IMEI" /><Value Type="Text">{{SearchValue}}</Value></Eq>{{DYNAMICSTRING}}</Or>';
var TwoOr = '<Or><Eq><FieldRef Name="IMEI" /><Value Type="Text">{{SearchValue}}</Value></Eq><Eq><FieldRef Name="IMEI" /><Value Type="Text">{{SearchValue}}</Value></Eq></Or>';
var OnlyEq =  '<Eq><FieldRef Name="IMEI" /><Value Type="Text">{{SearchValue}}</Value></Eq>';
function generateAdvancedInQuery(x){
    if(x.length == 1)
        return OnlyEq.replace('{{SearchValue}}',x[0]);
    else if(x.length == 2)
        return TwoOr.replace('{{SearchValue}}',x[0]).replace('{{SearchValue}}',x[1]);
    else 
        return OneOR.replace('{{SearchValue}}',x[x.length-1]).replace('{{DYNAMICSTRING}}',generateAdvancedInQuery(x.splice(0,x.length-1) ) );   
}

x = ['438753234098792','438753234098793','438753234098794','438753234098795','438753234098796','438753234098797','438753234098798'];

var Caml = DynamicQuery.replace('{{DYNAMICSTRING}}',generateAdvancedInQuery(x) )

This generates the XML caml query as

<Query>
    <Where>
        <Or>
            <Eq>
                <FieldRef Name="IMEI" />
                <Value Type="Text">438753234098798</Value>
            </Eq>
            <Or>
                <Eq>
                    <FieldRef Name="IMEI" />
                    <Value Type="Text">438753234098797</Value>
                </Eq>
                <Or>
                    <Eq>
                        <FieldRef Name="IMEI" />
                        <Value Type="Text">438753234098796</Value>
                    </Eq>
                    <Or>
                        <Eq>
                            <FieldRef Name="IMEI" />
                            <Value Type="Text">438753234098795</Value>
                        </Eq>
                        <Or>
                            <Eq>
                                <FieldRef Name="IMEI" />
                                <Value Type="Text">438753234098794</Value>
                            </Eq>
                            <Or>
                                <Eq>
                                    <FieldRef Name="IMEI" />
                                    <Value Type="Text">438753234098792</Value>
                                </Eq>
                                <Eq>
                                    <FieldRef Name="IMEI" />
                                    <Value Type="Text">438753234098793</Value>
                                </Eq>
                            </Or>
                        </Or>
                    </Or>
                </Or>
            </Or>
        </Or>
    </Where>
</Query>
Jaworski answered 18/4, 2019 at 12:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.