Коррелированный подзапрос и оконная функция - PullRequest
1 голос
/ 01 августа 2020

Я играл с образцом базы данных электронной коммерции, который я перенес с сервера SQL на GraphDB. Я перевел SQL запросов на SPARQL, и у меня уже есть несколько из них. Однако мне трудно делать такие вещи, как вычисление промежуточных итогов, скользящих средних, номеров строк и ранжирования и т. Д. c. Прежде чем я объясню два нижеприведенных подзапроса, вот фрагмент модели:

product -> hasCategory -> category
order -> hasCustomer -> customer

Первый запрос работает нормально. Однако мне нужно найти альтернативу второму, где мне нужно вернуть два самых последних заказа для каждого клиента во внешнем запросе. В SQL одним из решений было бы использовать коррелированный запрос (передать идентификатор клиента внутреннему запросу для каждой записи внешнего запроса).

Я заметил несколько «проблем / предложений» для SPARQL , зарегистрировано ниже:

Я вижу, вы, ребята, принимали участие в некоторых из них.

# Query 1: Select all products that belong to the Seafood category
# WORKS FINE, as the inner query is completely independent of the outer one.
SELECT
  ?productName
  ?unitPrice
  ?unitsInStock
WHERE { # outer query
  ?product a :product ;
             :productName ?productName ;
             :unitPrice ?unitPrice ;
             :unitsInStock ?unitsInStock ;
             :hasCategory ?category . 
    { # inner query
      SELECT 
        ?category
      WHERE {
        ?category a :category ;
                    :categoryID ?categoryID ;
                    :name "Seafood" .
      }
    }
  }
  ORDER BY
    ?productName


# Query 2: Select the 3 most recent orders of each customer  
# DOES NOT WORK!! It's a correlated query. SPARQL engine is not able to join the queries together. 
SELECT DISTINCT 
  ?customerID 
  ?city
WHERE { # outer query
  ?customer a :customer ;
              :customerID ?customerID ;
              :city ?city ;
              ^:hasCustomer ?order .
  { # inner query
    SELECT
        ?order
    WHERE {
      ?order a :order ;
               :orderID ?orderID ;
               :orderDate ?orderDate ;
               :hasCustomer ?customer .
    }
      ORDER BY
        DESC(?orderDate)
      LIMIT 3
    }
  }
ORDER BY
  ?customerID 
  ?city 
  DESC(?orderDate)

Я мог бы использовать OVER PARTITION BY для замены подзапроса выше с тем же результатом в SQL. Собственно, в SQL есть много способов сделать это. Но в SPARQL я ничего не нашел. Вы, ребята, знаете способ обхода этой проблемы?

Большое спасибо,

Марсело.

PS: Я также оставляю SQL запросы здесь для справки (то же самое варианты использования решаются по-разному).

-- Query: Select all products that belong to the Seafood category
SELECT
    prd.ProductName, 
    prd.UnitPrice, 
    prd.UnitsInStock
FROM 
    Product prd
WHERE 
    prd.CategoryID IN (
        SELECT ctg.CategoryID FROM Category ctg WHERE ctg.CategoryName = 'Seafood')
ORDER BY
    prd.ProductName


-- Query: Select all products that belong to the Seafood category
-- This query replaces the previous one by using the more efficient EXISTS
SELECT
    prd.ProductName, 
    prd.UnitPrice, 
    prd.UnitsInStock
FROM 
    Product prd
WHERE 
    EXISTS (
        SELECT 1 FROM Category ctg WHERE prd.CategoryID = ctg.CategoryID AND ctg.CategoryName = 'Seafood')
ORDER BY
    prd.ProductName


-- Query: Select all products that belong to the Seafood category
-- Re-writting the previous query using JOIN
SELECT
    prd.ProductName, 
    prd.UnitPrice, 
    prd.UnitsInStock
FROM 
    Product prd
    INNER JOIN Category ctg 
    ON prd.CategoryID = ctg.CategoryID 
    WHERE ctg.CategoryName = 'Seafood'
ORDER BY
    prd.ProductName


-- Query: Select the 3 most recent orders of each customer
SELECT 
    cst.CustomerID, 
    cst.City,
    cpp.OrderID, 
    cpp.OrderDate   
FROM 
    Customer AS cst
-- For each customer record, go and get the two most recent orders.
CROSS APPLY -- An INNER JOIN could've been used, however, CROSS APPLY is more efficient when combined with SELECT TOP.
(
    SELECT TOP 3 
        ord.OrderID, ord.OrderDate, cst.CustomerID
    FROM 
        [Order] AS ord
    WHERE 
        ord.customerid = cst.customerid -- reference to the outer query (correlated subquery)
    ORDER BY 
        ord.OrderDate DESC
) AS cpp
ORDER BY 
    cst.CustomerID, 
    cst.City,
    cpp.OrderDate DESC



-- Windowed Functions


-- Calculating row numbering and ranking, quantiles, moving averages, and running totals.
-- Reference: https://docs.microsoft.com/en-us/sql/t-sql/queries/select-over-clause-transact-sql?view=sql-server-2017


-- Query: Select the 3 most recent orders of each customer
-- This query replaces the previous one by using the more efficient Windowed Function. 
SELECT 
    ptt.*
FROM
(
    SELECT
    cst.CustomerID, 
    cst.City,
    ord.OrderID, 
    ord.OrderDate, 
    ROW_NUMBER() OVER(PARTITION BY cst.CustomerID ORDER BY ord.OrderDate DESC) AS [RowNumber]
    FROM Customer AS cst
    INNER JOIN [Order] AS ord 
    ON cst.CustomerID = ord.CustomerID
) ptt
WHERE 
    ptt.[RowNumber] <= 3
...