Answers.Body: <p>Here's how to prepare a Craft query that merge two channels of entries by distribute values evenly and setup the pagination system (with <a href="https://putyourlightson.com/plugins/sprig" rel="nofollow noreferrer">Sprig</a>):</p>
  <pre><code>{% set channelOne = craft.entries.section('channelOne').orderBy('title DESC').limit(null).ids() %}
  {% set channelTwo = craft.entries.section('channelTwo').orderBy('title DESC').limit(null).ids() %}
  
  {% set longest = (channelOne | length &gt; channelTwo | length) ? channelOne : channelTwo %}
  {% set shortest = (channelOne | length &gt; channelTwo | length) ? channelTwo : channelOne %}
  {% set ratio = (longest | length / shortest | length) | round(0, 'floor' %}
  {% set ids = [] %}
  
  {% if shortest is not empty %}
      {% for i in 0..(shortest | length - 1) %}
          {% for j in 0..ratio %}
              {% set ids = ids | merge([longest[i * ratio + j]]) %}
          {% endfor %}
          {% set ids = ids | merge([shortest[i]]) %}
      {% endfor %}
      {% for i in (longest | length - ((shortest is not empty) ? longest | length % shortest | length : longest | length))..(longest | length) %}
          {% set ids = ids | merge([longest[i - 1]]) %}
      {% endfor %}
  {% else %}
      {% set ids = longest %}
  {% endif %}
  
  {% set entryQuery = craft.entries.id(ids | unique).fixedOrder().offset(offset).limit(limit) %}
  {% set entries = entryQuery.all() %}
  </code></pre>
  <p><strong>Downside:</strong></p>
  <ul>
  <li>Because the ratio is not always a round number, you could see an
  unexpected batch of few entries at the end (from the <code>longest</code> entries)</li>
  </ul>
  <p><strong>What could be improved:</strong></p>
  <ul>
  <li>Refactore the Twig code for better readality and performance (as suggested by @MoritzLost: <em>write it in PHP and stick inside a Twig extension in a custom module that provides as a custom filter or function</em>)</li>
  <li>Eventually, and depending on the design, improve a little bit the display using CSS grid and/or JS if you would like to cheat the position in each row</li>
  </ul>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2022-09-13T15:45:05.997Z
Answers.LastActivityDate: 2022-09-13T15:50:25.067Z
Answers.LastEditDate: 2022-09-13T15:50:25.067Z
Answers.UserDisplayName: Romain P.
Answers.Body: <p>You can use one entry query to fetch entries from multiple sections:</p>
  <pre class="lang-twig prettyprint-override"><code>{% set entries = craft.entries()
      .section(['channel_one', 'channel_two'])
      .orderBy('postDate DESC')
      .limit(9)
      .all()
  %}
  </code></pre>
  <p>By using a single entry query that fetches entries from both channels, you can use <code>limit</code> and <code>offset</code> normally. The only requirement is that they are sorted by one shared field (like the native <code>postDate</code> property).</p>
  <p>For the display in rows and columns, I would just output a single list and display them using <a href="https://css-tricks.com/snippets/css/complete-guide-grid/" rel="nofollow noreferrer">CSS Grid</a>:</p>
  <pre class="lang-twig prettyprint-override"><code>&lt;ul class=&quot;entry-feed&quot;&gt;
      {% for entry in entries %}
          &lt;li class=&quot;entry-feed__item entry-feed__item--{{ entry.section.handle }}&quot;&gt;
              {{ entry.title }}
          &lt;/li&gt;
      {% endfor %}
  &lt;/ul&gt;
  </code></pre>
  <p>If you need to style the entries differently, this includes separate classes for both channels (i.e. <code>entry-feed__item--channel_one</code>). If you need to output different information for each channel, you can alter the display depending on the section:</p>
  <pre class="lang-twig prettyprint-override"><code>{% switch entry.section.handle %}
      {% case &quot;channel_one&quot; %}
          {{ entry.field_one }}
      {% case &quot;channel_two&quot; %}
          {{ entry.field_two }}
  {% endswitch %}
  </code></pre>
  <h2>Fixed number of entries per channel per page</h2>
  <p>If you need a fixed number of entries per channel on each page (always 6 entries from channel one and 3 entries from channel 2), you probably need two separate queries. You can't use the <code>paginate</code> tag in this case, you'll have to do the pagination yourself by applying both a limit and an offset. For example:</p>
  <pre class="lang-twig prettyprint-override"><code>{% set pageNumber = craft.app.request.pageNum ?? 1 %}
  {% set channelOneEntries = craft.entries()
      .section('channel_one')
      .orderBy('postDate DESC')
      .limit(6)
      .offset(pageNum - 1) * 6)
      .all()
  %}
  {% set channelTwoEntries = craft.entries()
      .section('channel_two')
      .orderBy('postDate DESC')
      .limit(3)
      .offset(pageNum - 1) * 3)
      .all()
  %}
  </code></pre>
  <p>Then you can mix those together into a single array you can loop over. How you do that depends on the desired outcome, i.e. what criteria you want the entries on a single page to be sorted by. For example, if you want a fixed position where the entries from channel two are always at indexes 1, 5 and 6:</p>
  <pre class="lang-twig prettyprint-override"><code>{% set entriesForCurrentPage = [
      channelOneEntries[0] ?? null,
      channelTwoEntries[0] ?? null,
      channelOneEntries[1] ?? null,
      channelOneEntries[2] ?? null,
      channelOneEntries[3] ?? null,
      channelTwoEntries[1] ?? null,
      channelTwoEntries[2] ?? null,
      channelOneEntries[4] ?? null,
      channelOneEntries[5] ?? null,
  ]|filter(v =&gt; v is not empty) %}
  </code></pre>
  <p>Of course, there are different ways to join the arrays, for example just mixing the entries randomly. This depends on your requirements.</p>
  <h2>Simple solutions for hard problems</h2>
  <p>You're looking for a simple solution to a complex problem – it just doesn't exist.</p>
  <blockquote>
  <p>I'm still not sure for the sort order, but I guess we can define it later using .orderBy()</p>
  </blockquote>
  <p>You can't if you're using your <code>fixedOrder</code> approach. In this case, the order is fixed.</p>
  <blockquote>
  <p>Isn't it possible to first get all ids (using .ids()) whatever the number of pages, order them as expected, then paginate them using .id(arrayOfIds).fixedOrder()</p>
  </blockquote>
  <p>Now your problem becomes recursive: To figure out what to order the query by, you want to order the items manually and then tell the query the order. But you still don't know exactly <em>how</em> to order your items. In your example, the entries from channel two were in positions 2, 6, and 7. But why that order? Is the order based on the post date, fixed, random? Figure out how your entries are supposed to be ordered, and you will know what to put in your query.</p>
  <blockquote>
  <p>I don't know how to get the best ratio and how to deal with decimal fractions while sorting the entries by batches. Your second example best match the problem but is using fixed number while I'm looking for an automatic ratio based on available totals. Also, I would need to make it works using one query, as I'm using pagination with Sprig.</p>
  </blockquote>
  <p>You can't have it all for free. You want a custom sort order with no single field being responsible for ordering the results, but you also want it to work nicely with paginated database queries and without having to do lots of complicated maths and array splitting. That's just not how structured queries work.</p>
  <p>Sure, your fixed order approach might work (I'm not even sure if pagination works with <code>fixedOrder</code>, but that's beside the point). But if you have already figured out the exact order of all entries across all pages, why do you need another query?</p>
  <p>In my opinion, you have two options:</p>
  <ol>
  <li>Write some complex logic to merge two sets of results (entries from channel one and two) based on your sorting rules. This will involve using the cardinality ratio to figure out how many items from both channels should go on each page, then writing a custom sorting algorithm to splice both arrays together in that way. That's definitely a lot of work, there's no single query parameter or simple function you can use to get this done, and I doubt someone here will be motivated do that work for you.</li>
  <li>Challenge the requirements. Which leads me to …</li>
  </ol>
  <h2>Frame challenge</h2>
  <p>In my experience, situations like this usually arise if you haven't sufficiently understood or challenged the requirements. For example, you haven't figured out how items should be sorted beyond the channel distribution. This is a sign that even if you somehow get this working, it will result in bad UX. If you don't know how entries are sorted, how will your visitors understand why entries are displayed in that particular order? Something important could be hiding on page 19, but why?</p>
  <p>It's very likely that your client expects <em>something</em> regarding the order that entries are presented in. Maybe they expect to be able to manually arrange which items should go on what page and in what order? In which case, you can either add a custom field for the sorting order or a global set with an entries field and use the order from that. Or maybe the client expects that the newest entries will be displayed first? In that case, you're back to ordering by the <code>postDate</code>.</p>
  <p>In both cases, questioning and challenging the requirements will lead to a simpler and better, more robust solution. And don't assume the client always <em>knows</em> their exact requirements, sometimes a request doesn't make sense. For example, maybe the client insists that items from both channels should be equally distributed across pages, but also wants items to be ordered chronologically (or manually). But this doesn't make sense – not because SQL doesn't support it, but because it doesn't work <em>logically</em>.</p>
  <p>Anyway, my recommendation would be to go back to the drawing board and figure out the motivation behind the feature you're building. What items are important and should be on page one? Why is item #1 in position one and item #5 in position five? Why are the items from channel two in positions 2, 6 and 7? Who makes that decision? Is it a manual or automatic process? Discuss these questions with your client, and I'm sure a simpler and better solution will present itself.</p>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2022-09-09T14:31:44.410Z
Answers.Text: Thank you, everything makes sense and it's what I had in mind. But the issue is that I can't rely on `postDate DESC`, that's too tricky to maintain. I really need that each paginated page show exactly the same amount of `channel_one` & `channel_two`: that's the main issue as they can have different number of entries. Also, as the list of entries will be links, use the flex order or grid start / end would perhaps give an inappropriate order on keyboard use (accessibility), so I would prefer to loop with Twig using the correct displayed order.
Answers.UserDisplayName: Romain P.
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2022-09-09T15:01:25.090Z
Answers.Text: @RomainPoirier Ok, if you need a fixed amount of entries from each channel per page, you can't use a single query. In this case, I would use two queries; one to fetch six entries from channel 1, one to fetch 3 entries from channel 2. Limit and offset both accordingly. Then merge those into a single array (how you do that depends on how you want them merged, i.e. what rules determine the position) and then use Twig to output them in order. Great that you care about keyboard navigation! You're right, it's easier to output the links in order (though using `tabindex` would be a workaround).
Answers.UserDisplayName: MoritzLost
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2022-09-09T15:35:33.730Z
Answers.Text: Ok thank you, I understand that I first should get the entries ids, then prepare a final query based on id + fixedOrder param? Will it work with paginate? The other challenge is that the group of 3 entries / 6 entries was just an example. It should be adapted based on the available entries of each channel, that will have a variable total of entries. But I need to display each time a total of 9 entries during the paginate, which could be tricky to divide (odd number). Do you have any idea of the calc to always get 9 BUT keep the layout as explained (so try to display them equally in the grid)?
Answers.UserDisplayName: Romain P.
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2022-09-09T17:37:34.120Z
Answers.Text: @RomainPoirier You will never be able to get the results in a single query, because there's no single parameter you can sort by to always get 3 and 6 entries from each channel on one page, respectively. The only way to do that would be to use a fixed sequence of entry IDs, at which point you already know the result, so why bother. So you can't use the paginate tag, which means you need to calculate the offset manually. I've updated my answer with some example code. The best solution really depends a lot on specific details of the requirements.
Answers.UserDisplayName: MoritzLost
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2022-09-12T08:11:25.880Z
Answers.Text: Thank you for your answer, I appreciate. However, I have to precise: don't focus on the 6 and 3 iterations, that was an example. I need 9 items per page, but the 6 and 3 should be defined based on the total of both channels. If they have the same total of entries, the ratio should be 1 to 1. If we have 20 entries from channelOne and 54 from channelTwo, we should show first channelTwo and add a channelOne maybe each 3 channelTwo's entries. How to calculate and add this while preventing missing entries because of non-even calc?
Answers.UserDisplayName: Romain P.
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2022-09-12T08:13:09.607Z
Answers.Text: Also, the pagination is a requirement: I'm using Sprig and have to load entries by batch of 9 entries. Isn't it possible to preprocess the order by getting and merge ids, then create another query like `craft.entries.id(mergedIds).fixedOrder().offset(offset).limit(limit)` to get the expected order of merged channels?
Answers.UserDisplayName: Romain P.
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2022-09-13T10:30:08.007Z
Answers.Text: @RomainPoirier Hm, difficult. Sounds like the requirements aren't really thought out to the end yet. How should items be distributed depending on the number of entries? What if one section has no items. What if the distribution shifts over time? How should entries be sorted within one page? All those question can be used to tease out a better data structure that will work well with your query and your Sprig setup. If you want to get this to work with one query, you'll either need a single parameter to sort by, or do a lot of complicated calculations by hand (using your fixed order approach).
Answers.UserDisplayName: MoritzLost
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2022-09-13T10:43:11.680Z
Answers.Text: Requirements are set: it's not static, and the sequence should be flexible. The items should be distributed equally, based on their respective total. If there's for example 70% of channelOne and 30% of ChannelTwo, that should be reflected in the paginated sequence and distributed equaly. If one section has no item, there is no change in the sequence, we're just showing the entries as if there's only one channel. The distribution should be automated, so it will probably change. The entries should be sorted based on a custom field and/or their title.
Answers.UserDisplayName: Romain P.
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2022-09-13T11:12:22.240Z
Answers.Text: @RomainPoirier But how are the items sorted, both for the pagination and within one page? A pagination makes no sense without a logical sort order. Anyway, in this case, I would use a [`count()` query](https://craftcms.com/docs/4.x/element-queries.html#count) to get the number of elements in both sections and use that to calculate the ratio. Then apply this ratio to get the number of entries from each section per channel. Then you can use the approach with two separate queries in my updated answer above.
Answers.UserDisplayName: MoritzLost
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2022-09-13T12:55:05.280Z
Answers.Text: I'm still not sure for the sort order, but I guess we can define it later using .orderBy(). I don't know how to get the best ratio and how to deal with decimal fractions while sorting the entries by batches. Your second example best match the problem but is using fixed number while I'm looking for an automatic ratio based on available totals. Also, I would need to make it works using one query, as I'm using pagination with Sprig. Isn't it possible to first get all ids (using `.ids()`) whatever the number of pages, order them as expected, then paginate them using `.id(arrayOfIds).fixedOrder()`?
Answers.UserDisplayName: Romain P.
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2022-09-13T14:12:06.070Z
Answers.Text: @RomainPoirier You're looking for a simple solution to a hard problem, and I don't think it exists. I've added two more sections to my answer, hopefully that'll help.
Answers.UserDisplayName: MoritzLost
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2022-09-13T14:58:06.630Z
Answers.Text: I finally found this, which looks like what I'm trying to get as merged sequence: https://stackoverflow.com/questions/62888437/efficiently-merge-two-arrays-by-distribute-values-evenly. I'm trying to transpose it to Twig. After that, like I thought,  I should get all ids using .ids(),  then prepare a single query using .id(arrayOfIds).fixedOrder(). After that, a little bit or Twig and/or CSS and/or JS could enventually improve the display. I will update later, but this doesn't look like impossible.
Answers.UserDisplayName: Romain P.
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2022-09-13T15:24:39.957Z
Answers.Text: @RomainPoirier Don't do stuff like that in Twig, write it in PHP and stick inside a [Twig extension in a custom module](https://craftcms.com/docs/4.x/extend/extending-twig.html#register-a-twig-extension) that provides as a custom filter or function. Much easier and simpler to read in Twig. I'm not saying it's impossible, just complicated, and likely to be wasted effort.
Answers.UserDisplayName: MoritzLost
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2022-09-09T13:49:30.297Z
Answers.LastActivityDate: 2022-09-13T14:12:29.310Z
Answers.LastEditDate: 2022-09-13T14:12:29.310Z
Answers.UserDisplayName: MoritzLost
Body: <p>I have entries from two different channels, where each channel has a different total of entries, and I want to display them equally by batch.</p>
  <p>Example:</p>
  <pre><code>Channel #1 | Channel #2 | Channel #1
  Channel #1 | Channel #1 | Channel #2
  Channel #2 | Channel #1 | Channel #1
  </code></pre>
  <p>Assuming here we've got 9 entries, dispatched in 6 entries from <code>Channel #1</code> and 3 entries from <code>Channel #2</code>.</p>
  <p>Requirements:</p>
  <ul>
  <li>I'm using a pagination system with <a href="https://putyourlightson.com/plugins/sprig" rel="nofollow noreferrer">Sprig</a>, so I'm not loading all
  entries at first load, only by batch of 9 entries: this should be
  taken in consideration as channel entries should be distributed
  equally during the pagination.</li>
  <li>If possible, the different entries should be dispatched on each row, so there are not displayed together (as they have different styles)</li>
  </ul>
  <p>How can I do that using craft entries query?</p>
  
Tags: <pagination><channels><merge><layout><plugin-sprig>
Title: How to dispatch equally entries from different channels?
ThreadText: # How to dispatch equally entries from different channels?
  
  (Asked by: username_0 on 2022-09-09)
  
  I have entries from two different channels, where each channel has a different total of entries, and I want to display them equally by batch.
  
  Example:
  
  ```
  Channel #1 | Channel #2 | Channel #1
  Channel #1 | Channel #1 | Channel #2
  Channel #2 | Channel #1 | Channel #1
  
  ```
  
  Assuming here we've got 9 entries, dispatched in 6 entries from `Channel #1` and 3 entries from `Channel #2`.
  
  Requirements:
  
  * I'm using a pagination system with [Sprig](https://putyourlightson.com/plugins/sprig), so I'm not loading all
    entries at first load, only by batch of 9 entries: this should be
    taken in consideration as channel entries should be distributed
    equally during the pagination.
  * If possible, the different entries should be dispatched on each row, so there are not displayed together (as they have different styles)
  
  How can I do that using craft entries query?
  
  **Tags:** pagination, channels, merge, layout, plugin-sprig
  
  
  ---
  
  ## 2 answers
  
  ### Answer by username_0 on 2022-09-13. Score: 0 | Accepted answer
  
  Here's how to prepare a Craft query that merge two channels of entries by distribute values evenly and setup the pagination system (with [Sprig](https://putyourlightson.com/plugins/sprig)):
  
  ```
  {% set channelOne = craft.entries.section('channelOne').orderBy('title DESC').limit(null).ids() %}
  {% set channelTwo = craft.entries.section('channelTwo').orderBy('title DESC').limit(null).ids() %}
  
  {% set longest = (channelOne | length > channelTwo | length) ? channelOne : channelTwo %}
  {% set shortest = (channelOne | length > channelTwo | length) ? channelTwo : channelOne %}
  {% set ratio = (longest | length / shortest | length) | round(0, 'floor' %}
  {% set ids = [] %}
  
  {% if shortest is not empty %}
      {% for i in 0..(shortest | length - 1) %}
          {% for j in 0..ratio %}
              {% set ids = ids | merge([longest[i * ratio + j]]) %}
          {% endfor %}
          {% set ids = ids | merge([shortest[i]]) %}
      {% endfor %}
      {% for i in (longest | length - ((shortest is not empty) ? longest | length % shortest | length : longest | length))..(longest | length) %}
          {% set ids = ids | merge([longest[i - 1]]) %}
      {% endfor %}
  {% else %}
      {% set ids = longest %}
  {% endif %}
  
  {% set entryQuery = craft.entries.id(ids | unique).fixedOrder().offset(offset).limit(limit) %}
  {% set entries = entryQuery.all() %}
  
  ```
  
  **Downside:**
  
  * Because the ratio is not always a round number, you could see an
    unexpected batch of few entries at the end (from the `longest` entries)
  
  **What could be improved:**
  
  * Refactore the Twig code for better readality and performance (as suggested by @username_1: *write it in PHP and stick inside a Twig extension in a custom module that provides as a custom filter or function*)
  * Eventually, and depending on the design, improve a little bit the display using CSS grid and/or JS if you would like to cheat the position in each row
  
  
  ---
  
  ### Answer by username_1 on 2022-09-09. Score: 1
  
  You can use one entry query to fetch entries from multiple sections:
  
  ```twig
  {% set entries = craft.entries()
      .section(['channel_one', 'channel_two'])
      .orderBy('postDate DESC')
      .limit(9)
      .all()
  %}
  
  ```
  
  By using a single entry query that fetches entries from both channels, you can use `limit` and `offset` normally. The only requirement is that they are sorted by one shared field (like the native `postDate` property).
  
  For the display in rows and columns, I would just output a single list and display them using [CSS Grid](https://css-tricks.com/snippets/css/complete-guide-grid/):
  
  ```twig
  <ul class="entry-feed">
      {% for entry in entries %}
          <li class="entry-feed__item entry-feed__item--{{ entry.section.handle }}">
              {{ entry.title }}
          </li>
      {% endfor %}
  </ul>
  
  ```
  
  If you need to style the entries differently, this includes separate classes for both channels (i.e. `entry-feed__item--channel_one`). If you need to output different information for each channel, you can alter the display depending on the section:
  
  ```twig
  {% switch entry.section.handle %}
      {% case "channel_one" %}
          {{ entry.field_one }}
      {% case "channel_two" %}
          {{ entry.field_two }}
  {% endswitch %}
  
  ```
  
  ## Fixed number of entries per channel per page
  
  If you need a fixed number of entries per channel on each page (always 6 entries from channel one and 3 entries from channel 2), you probably need two separate queries. You can't use the `paginate` tag in this case, you'll have to do the pagination yourself by applying both a limit and an offset. For example:
  
  ```twig
  {% set pageNumber = craft.app.request.pageNum ?? 1 %}
  {% set channelOneEntries = craft.entries()
      .section('channel_one')
      .orderBy('postDate DESC')
      .limit(6)
      .offset(pageNum - 1) * 6)
      .all()
  %}
  {% set channelTwoEntries = craft.entries()
      .section('channel_two')
      .orderBy('postDate DESC')
      .limit(3)
      .offset(pageNum - 1) * 3)
      .all()
  %}
  
  ```
  
  Then you can mix those together into a single array you can loop over. How you do that depends on the desired outcome, i.e. what criteria you want the entries on a single page to be sorted by. For example, if you want a fixed position where the entries from channel two are always at indexes 1, 5 and 6:
  
  ```twig
  {% set entriesForCurrentPage = [
      channelOneEntries[0] ?? null,
      channelTwoEntries[0] ?? null,
      channelOneEntries[1] ?? null,
      channelOneEntries[2] ?? null,
      channelOneEntries[3] ?? null,
      channelTwoEntries[1] ?? null,
      channelTwoEntries[2] ?? null,
      channelOneEntries[4] ?? null,
      channelOneEntries[5] ?? null,
  ]|filter(v => v is not empty) %}
  
  ```
  
  Of course, there are different ways to join the arrays, for example just mixing the entries randomly. This depends on your requirements.
  
  ## Simple solutions for hard problems
  
  You're looking for a simple solution to a complex problem – it just doesn't exist.
  
  > I'm still not sure for the sort order, but I guess we can define it later using .orderBy()
  
  You can't if you're using your `fixedOrder` approach. In this case, the order is fixed.
  
  > Isn't it possible to first get all ids (using .ids()) whatever the number of pages, order them as expected, then paginate them using .id(arrayOfIds).fixedOrder()
  
  Now your problem becomes recursive: To figure out what to order the query by, you want to order the items manually and then tell the query the order. But you still don't know exactly *how* to order your items. In your example, the entries from channel two were in positions 2, 6, and 7. But why that order? Is the order based on the post date, fixed, random? Figure out how your entries are supposed to be ordered, and you will know what to put in your query.
  
  > I don't know how to get the best ratio and how to deal with decimal fractions while sorting the entries by batches. Your second example best match the problem but is using fixed number while I'm looking for an automatic ratio based on available totals. Also, I would need to make it works using one query, as I'm using pagination with Sprig.
  
  You can't have it all for free. You want a custom sort order with no single field being responsible for ordering the results, but you also want it to work nicely with paginated database queries and without having to do lots of complicated maths and array splitting. That's just not how structured queries work.
  
  Sure, your fixed order approach might work (I'm not even sure if pagination works with `fixedOrder`, but that's beside the point). But if you have already figured out the exact order of all entries across all pages, why do you need another query?
  
  In my opinion, you have two options:
  
  1. Write some complex logic to merge two sets of results (entries from channel one and two) based on your sorting rules. This will involve using the cardinality ratio to figure out how many items from both channels should go on each page, then writing a custom sorting algorithm to splice both arrays together in that way. That's definitely a lot of work, there's no single query parameter or simple function you can use to get this done, and I doubt someone here will be motivated do that work for you.
  2. Challenge the requirements. Which leads me to …
  
  ## Frame challenge
  
  In my experience, situations like this usually arise if you haven't sufficiently understood or challenged the requirements. For example, you haven't figured out how items should be sorted beyond the channel distribution. This is a sign that even if you somehow get this working, it will result in bad UX. If you don't know how entries are sorted, how will your visitors understand why entries are displayed in that particular order? Something important could be hiding on page 19, but why?
  
  It's very likely that your client expects *something* regarding the order that entries are presented in. Maybe they expect to be able to manually arrange which items should go on what page and in what order? In which case, you can either add a custom field for the sorting order or a global set with an entries field and use the order from that. Or maybe the client expects that the newest entries will be displayed first? In that case, you're back to ordering by the `postDate`.
  
  In both cases, questioning and challenging the requirements will lead to a simpler and better, more robust solution. And don't assume the client always *knows* their exact requirements, sometimes a request doesn't make sense. For example, maybe the client insists that items from both channels should be equally distributed across pages, but also wants items to be ordered chronologically (or manually). But this doesn't make sense – not because SQL doesn't support it, but because it doesn't work *logically*.
  
  Anyway, my recommendation would be to go back to the drawing board and figure out the motivation behind the feature you're building. What items are important and should be on page one? Why is item #1 in position one and item #5 in position five? Why are the items from channel two in positions 2, 6 and 7? Who makes that decision? Is it a manual or automatic process? Discuss these questions with your client, and I'm sure a simpler and better solution will present itself.
  
  > **Comments:**
  > **username_0**: Thank you, everything makes sense and it's what I had in mind. But the issue is that I can't rely on `postDate DESC`, that's too tricky to maintain. I really need that each paginated page show exactly the same amount of `channel_one` & `channel_two`: that's the main issue as they can have different number of entries. Also, as the list of entries will be links, use the flex order or grid start / end would perhaps give an inappropriate order on keyboard use (accessibility), so I would prefer to loop with Twig using the correct displayed order.  
  > **username_1**: @RomainPoirier Ok, if you need a fixed amount of entries from each channel per page, you can't use a single query. In this case, I would use two queries; one to fetch six entries from channel 1, one to fetch 3 entries from channel 2. Limit and offset both accordingly. Then merge those into a single array (how you do that depends on how you want them merged, i.e. what rules determine the position) and then use Twig to output them in order. Great that you care about keyboard navigation! You're right, it's easier to output the links in order (though using `tabindex` would be a workaround).  
  > **username_0**: Ok thank you, I understand that I first should get the entries ids, then prepare a final query based on id + fixedOrder param? Will it work with paginate? The other challenge is that the group of 3 entries / 6 entries was just an example. It should be adapted based on the available entries of each channel, that will have a variable total of entries. But I need to display each time a total of 9 entries during the paginate, which could be tricky to divide (odd number). Do you have any idea of the calc to always get 9 BUT keep the layout as explained (so try to display them equally in the grid)?  
  > **username_1**: @RomainPoirier You will never be able to get the results in a single query, because there's no single parameter you can sort by to always get 3 and 6 entries from each channel on one page, respectively. The only way to do that would be to use a fixed sequence of entry IDs, at which point you already know the result, so why bother. So you can't use the paginate tag, which means you need to calculate the offset manually. I've updated my answer with some example code. The best solution really depends a lot on specific details of the requirements.  
  > **username_0**: Thank you for your answer, I appreciate. However, I have to precise: don't focus on the 6 and 3 iterations, that was an example. I need 9 items per page, but the 6 and 3 should be defined based on the total of both channels. If they have the same total of entries, the ratio should be 1 to 1. If we have 20 entries from channelOne and 54 from channelTwo, we should show first channelTwo and add a channelOne maybe each 3 channelTwo's entries. How to calculate and add this while preventing missing entries because of non-even calc?  
  > **username_0**: Also, the pagination is a requirement: I'm using Sprig and have to load entries by batch of 9 entries. Isn't it possible to preprocess the order by getting and merge ids, then create another query like `craft.entries.id(mergedIds).fixedOrder().offset(offset).limit(limit)` to get the expected order of merged channels?  
  > **username_1**: @RomainPoirier Hm, difficult. Sounds like the requirements aren't really thought out to the end yet. How should items be distributed depending on the number of entries? What if one section has no items. What if the distribution shifts over time? How should entries be sorted within one page? All those question can be used to tease out a better data structure that will work well with your query and your Sprig setup. If you want to get this to work with one query, you'll either need a single parameter to sort by, or do a lot of complicated calculations by hand (using your fixed order approach).  
  > **username_0**: Requirements are set: it's not static, and the sequence should be flexible. The items should be distributed equally, based on their respective total. If there's for example 70% of channelOne and 30% of ChannelTwo, that should be reflected in the paginated sequence and distributed equaly. If one section has no item, there is no change in the sequence, we're just showing the entries as if there's only one channel. The distribution should be automated, so it will probably change. The entries should be sorted based on a custom field and/or their title.  
  > **username_1**: @RomainPoirier But how are the items sorted, both for the pagination and within one page? A pagination makes no sense without a logical sort order. Anyway, in this case, I would use a [`count()` query](https://craftcms.com/docs/4.x/element-queries.html#count) to get the number of elements in both sections and use that to calculate the ratio. Then apply this ratio to get the number of entries from each section per channel. Then you can use the approach with two separate queries in my updated answer above.  
  > **username_0**: I'm still not sure for the sort order, but I guess we can define it later using .orderBy(). I don't know how to get the best ratio and how to deal with decimal fractions while sorting the entries by batches. Your second example best match the problem but is using fixed number while I'm looking for an automatic ratio based on available totals. Also, I would need to make it works using one query, as I'm using pagination with Sprig. Isn't it possible to first get all ids (using `.ids()`) whatever the number of pages, order them as expected, then paginate them using `.id(arrayOfIds).fixedOrder()`?  
  > **username_1**: @RomainPoirier You're looking for a simple solution to a hard problem, and I don't think it exists. I've added two more sections to my answer, hopefully that'll help.  
  > **username_0**: I finally found this, which looks like what I'm trying to get as merged sequence: https://stackoverflow.com/questions/62888437/efficiently-merge-two-arrays-by-distribute-values-evenly. I'm trying to transpose it to Twig. After that, like I thought,  I should get all ids using .ids(),  then prepare a single query using .id(arrayOfIds).fixedOrder(). After that, a little bit or Twig and/or CSS and/or JS could enventually improve the display. I will update later, but this doesn't look like impossible.  
  > **username_1**: @RomainPoirier Don't do stuff like that in Twig, write it in PHP and stick inside a [Twig extension in a custom module](https://craftcms.com/docs/4.x/extend/extending-twig.html#register-a-twig-extension) that provides as a custom filter or function. Much easier and simpler to read in Twig. I'm not saying it's impossible, just complicated, and likely to be wasted effort.  
  
  ---
  
  