SQL: как сделать запрос только 1 раз вместо зацикливания дат? - PullRequest
0 голосов
/ 06 ноября 2019

У меня есть такая таблица:

---------------------------------------
product_id | valid_from  | valid_till |
1          |  2018-01-01 | 2018-09-01 |
2          |  2013-03-01 | 2019-07-01 |
---------------------------------------

...

Я хочу получить все действительные продукты для списка с указанными датами и положитьих в map<Date, List<Product>>:

так что я делаю:

Map<Date, List<Product>> map = new HashMap<>();
for (Date date : list_date) {
    Map<String, Object> param = new HashMap<>();
    param.put("date", date);
    List<Product> products = this.queryForList("getProductForDate", param);
    map.put(date, products);
}

и мой запрос:

select product_id
from my_table
where date >= valid_from and date < valid_till

но я хочу знать, этоможно сделать запрос 1 раз только вместо цикла по датам в коде? Как справиться с предложением where в этом случае?

Ответы [ 2 ]

0 голосов
/ 13 ноября 2019

Использование CTE может быть самым простым решением.

Вы можете передать список дат в качестве параметра.

Map<Date, List<Product>> map = new HashMap<>();
Map<String, Object> param = new HashMap<>();
param.put("dates", list_date);
List<Map<String, ?>> result = this.queryForList("getProductForDate", param);

Оператор и карта результатов могут выглядеть следующим образом:

<select id="getValidProductsInternal" resultMap="productsPerDate">
  with dates(d) as (
    <foreach item="date" collection="dates" separator="union all">
      select #{date,jdbcType=DATE} as d from dual
    </foreach>
  )
  select dates.d date_in_question, t.product_id
  from dates
  left join my_table t on
    dates.d >= t.valid_from and t.valid_till > dates.d
</select>

<resultMap type="map" id="productsPerDate">
  <id column="date_in_question" property="date" />
  <collection property="products" javaType="list"
      ofType="test.Product">
    <id column="product_id" property="id" />
  </collection>
</resultMap>

Объяснение CTE может быть не по теме, а проще говоря, предложение with создает виртуальную таблицу (своего рода) dates с одним столбцом d типа DATE.
Создается строка для каждого Date в параметре dates, поэтому, если вы передадите три Date s в качестве параметра, в таблице dates будет три строки.
select ... left joinчасть может быть простой.

Теперь результат может содержать каждую дату в списке, даже если на эту дату нет действительного продукта.
Например ...

[
  {
    date : 2019-01-01,
    products : [
     {id: 2, ...}
    ]
  },
  {
    date : 2019-01-03,
    products : [
      {id: 1, ...},
      {id: 2, ...}
    ]
  },
  {
    date : 2019-01-05,
    products : []
  },
  ...
]

Хотя этот результат не совсем то, что вы хотите, его легко конвертировать.

Map<Date, List<Product>> validProductsPerDate = result.stream()
  .collect(Collectors.toMap(
    e -> (Date) e.get("date"),
    e -> (List<Product>) e.get("products")));

Вот исполняемый файл demo .
Демонстрация использует интерфейс mapper и включает в себя как примеры случайных, так и последовательных дат.

0 голосов
/ 06 ноября 2019

Я знаю, что Mybatis имеет метод selectMap в интерфейсе SqlSession , Но я никогда не использовал его , Вы можете попробовать это сами. Я считаю, что это возможно.

...