# Advanced Data Pulling (For Admins)

## What You'll Learn

In this tutorial, you'll build a simple quote summary that pulls one reusable result from a SOQL query. By the end, you'll know how to:

* Create a named query with `<queryname>`
* Return data without rendering a table
* Reuse query results with `{{!QueryName.FieldName}}`
* Control which row is returned with `WHERE`, `ORDER BY`, and `LIMIT`
* Test and troubleshoot blank named query output

## What You'll Build

An Opportunity template that includes:

* The highest-value line item name
* The quantity for that line item
* The total price for that line item
* Reusable values you can place in the body, header, or footer

**Estimated time:** 20 minutes

## Prerequisites

Before starting, ensure you have:

* Access to S-Docs Templates in Salesforce
* Permission to create or edit templates
* An S-Docs template related to `Opportunity`
* At least one `OpportunityLineItem` record on your test opportunity
* Basic familiarity with merge fields such as `{{!Opportunity.Name}}`

If you are brand new to S-Docs syntax, complete a basic merge field tutorial first.

{% stepper %}
{% step %}
**Step 1: Open or Create Your Template**

Start with an Opportunity template where you want to show data outside a table.

1. Navigate to **S-Docs Templates** in Salesforce
2. Open an existing template or click **New Template**
3. Choose `Opportunity` as the related object
4. Open the template editor
5. Click **Source** if you want to paste the query directly

You now have a place to add a named query.
{% endstep %}

{% step %}
**Step 2: Add Your First Named Query Block**

Named queries use a SOQL block plus a reusable query name.

Add this block near the top of your template:

```xml
<!--{{!
<lineitemsSOQL>
  <class>none</class>
  <queryname>TopLineItem</queryname>
  <soql>
    SELECT Name, Quantity, TotalPrice
    FROM OpportunityLineItem
    WHERE OpportunityId = '{{!Opportunity.Id}}'
    ORDER BY TotalPrice DESC
    LIMIT 1
  </soql>
</lineitemsSOQL>
}}-->
```

What you just did:

* Used `<class>none</class>` to stop S-Docs from rendering a table
* Used `<queryname>TopLineItem</queryname>` to store the result under one name
* Used `<soql>` to return one line item for the current opportunity

How to read it:

* `WHERE OpportunityId = '{{!Opportunity.Id}}'` ties the query to the current record
* `ORDER BY TotalPrice DESC` sorts the largest line item first
* `LIMIT 1` returns only that first row

{% hint style="warning" %}
The query name is case-sensitive.

Use the exact same value later in your merge fields.
{% endhint %}
{% endstep %}

{% step %}
**Step 3: Reuse the Returned Values**

Now place merge fields anywhere below the query block.

```plaintext
Top line item: {{!TopLineItem.Name}}
Quantity: {{!TopLineItem.Quantity}}
Total: ${{!TopLineItem.TotalPrice #,###.00}}
```

What you just did:

* Referenced the named query with `TopLineItem`
* Pulled field values from the query result
* Formatted the total as currency

The merge field pattern is:

`{{!QueryName.FieldName}}`

If the query name or field name does not match, the output will be blank.
{% endstep %}

{% step %}
**Step 4: Understand What Makes the Output Work**

Named queries only expose fields that you include in the `SELECT` clause.

In this example:

* `TopLineItem` comes from `<queryname>`
* `Name`, `Quantity`, and `TotalPrice` come from `SELECT`
* The query block must appear before the merge fields that use it

If you want to output another field later, add it to the query first.

Example:

```xml
SELECT Name, Quantity, TotalPrice, UnitPrice
```

Then you can use:

```plaintext
Unit price: ${{!TopLineItem.UnitPrice #,###.00}}
```

{% endstep %}

{% step %}
**Step 5: Change Which Record the Query Returns**

Named queries are useful because you control exactly which row is stored.

Try this variation if you want the lowest-priced line item instead:

```xml
<!--{{!
<lineitemsSOQL>
  <class>none</class>
  <queryname>LowestLineItem</queryname>
  <soql>
    SELECT Name, Quantity, TotalPrice
    FROM OpportunityLineItem
    WHERE OpportunityId = '{{!Opportunity.Id}}'
    ORDER BY TotalPrice ASC
    LIMIT 1
  </soql>
</lineitemsSOQL>
}}-->
```

What changed:

* `ORDER BY TotalPrice ASC` sorts from smallest to largest
* `LowestLineItem` creates a second reusable query result

Use `WHERE`, `ORDER BY`, and `LIMIT` together when you need one specific row.
{% endstep %}

{% step %}
**Step 6: Use the Same Query Result in Multiple Places**

Once a named query runs, you can reuse it anywhere later in the template.

Example:

```html
<h2>Quote Summary</h2>
<p>Top line item: {{!TopLineItem.Name}}</p>
<p>Quantity: {{!TopLineItem.Quantity}}</p>
<p>Total: ${{!TopLineItem.TotalPrice #,###.00}}</p>
<p>This quote is led by {{!TopLineItem.Name}}.</p>
```

What you just did:

* Ran the SOQL once
* Reused the same values several times
* Kept the template shorter and easier to maintain

This is the main reason to use named queries instead of repeating logic.
{% endstep %}

{% step %}
**Step 7: Save and Test Your Template**

Now test the query with a real opportunity.

1. Click **Save** in the template editor
2. Generate the template from an opportunity with line items
3. Confirm the highest-value line item appears
4. Compare the output against the actual line items on the opportunity

What to verify:

* The template runs from `Opportunity`
* The opportunity has at least one line item
* The query includes every field you reference later
* The query name matches your merge fields exactly
  {% endstep %}

{% step %}
**Step 8: Experiment with Variations**

Once the basics work, try a few common variations.

**Variation 1: Return a relationship field**

```xml
SELECT PricebookEntry.Product2.Name, Quantity, TotalPrice
FROM OpportunityLineItem
WHERE OpportunityId = '{{!Opportunity.Id}}'
ORDER BY TotalPrice DESC
LIMIT 1
```

Then output:

```plaintext
Product: {{!TopLineItem.PricebookEntry.Product2.Name}}
```

**Variation 2: Filter to a smaller set of rows**

```xml
SELECT Name, Quantity, TotalPrice
FROM OpportunityLineItem
WHERE OpportunityId = '{{!Opportunity.Id}}'
AND Quantity > 1
ORDER BY TotalPrice DESC
LIMIT 1
```

**Variation 3: Add another field to the same query**

```xml
SELECT Name, Quantity, TotalPrice, ServiceDate
FROM OpportunityLineItem
WHERE OpportunityId = '{{!Opportunity.Id}}'
ORDER BY TotalPrice DESC
LIMIT 1
```

Then output:

```plaintext
Service date: {{!TopLineItem.ServiceDate}}
```

Try one variation at a time, then regenerate the document to confirm the result.
{% endstep %}
{% endstepper %}

## Common Issues and Solutions

<details>

<summary>Issue 1: "The merge fields are blank"</summary>

Problem: The named query block runs, but the output fields do not show values.

Solution:

* Verify the query actually returns a row for the current record
* Check that the query name matches exactly, including capitalization
* Confirm every referenced field is included in the `SELECT` clause
* Make sure the merge fields appear below the named query block

</details>

<details>

<summary>Issue 2: "A table shows up instead of plain text"</summary>

Problem: The query output renders like a related list.

Solution:

* Make sure the block uses `<class>none</class>`
* Check that you did not replace it with a table class name
* Regenerate the document after saving the change

</details>

<details>

<summary>Issue 3: "The wrong row is returned"</summary>

Problem: You expected one record, but a different row appears.

Solution:

* Review the `WHERE` clause first
* Check the `ORDER BY` direction, `ASC` or `DESC`
* Use `LIMIT 1` when you only want one row
* Test the SOQL logic against real record data

</details>

<details>

<summary>Issue 4: "A relationship field is blank"</summary>

Problem: A field like product name does not show in the output.

Solution:

* Include the full relationship path in `SELECT`
* Use the same full path in the merge field
* Verify the related record actually exists on that row

</details>

<details>

<summary>Issue 5: "The query works in one place but not another"</summary>

Problem: Some references resolve, but earlier ones do not.

Solution:

* Place the named query before every section that uses it
* Avoid referencing the query result above the query block
* Keep the query block near the top of the template when possible

</details>

## What You've Learned

Congratulations! You've built a template with a reusable named query and learned:

✅ How to create a named query with `<queryname>`\
✅ How to prevent table output with `<class>none</class>`\
✅ How to reuse query values with `{{!QueryName.FieldName}}`\
✅ How to control the returned row with `WHERE`, `ORDER BY`, and `LIMIT`\
✅ How to test and troubleshoot blank output

## Next Steps

Now that you've mastered the basics, you can:

### Expand Your Queries

* Pull relationship fields into your output
* Return a different row with sorting and filters
* Add more fields to one reusable query
* Build multiple named queries in one template

### Combine with Other Features

* Use named queries with conditional logic
* Pair named queries with calculations
* Reuse named query output inside component templates
* Mix named queries with standard merge fields

### Practice More

* Build a quote summary for the top-priced line item
* Show a service date only for the returned line item
* Add a second named query for a different product row

**Recommended reading:**

* Named Query Working Examples
* How to: Advanced Data Retrieval with Named Queries
* Named Query Structure
* Named Queries In Microsoft Templates (DOCX, PPTX, XLSX)

## Practice Exercise

To reinforce what you've learned, build an Opportunity template that includes:

1. A named query for the highest-value `OpportunityLineItem`
2. The line item name, quantity, and total price in plain text
3. One repeated use of the same query result in a second section
4. A variation that returns the lowest-priced line item instead
5. A test using one opportunity that has line items and one that does not

Bonus challenges:

* Add a relationship field such as `PricebookEntry.Product2.Name`
* Add another field to the same query and output it later
* Create a second named query with a different filter


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://help.sdocs.com/quick-start/template-building/creating-your-first-named-query-tutorial.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
