Hello,

At the beginning I would like to agree with that the problem is iterative rather then recursive one. However,

On 2018-05-01 14:15, R Smith wrote:
That depends on what you mean by "Could this be achieved in SQLite?".

There is no query (in any SQL engine) that can depend on a sub-query that is itself dependent on the outcome of the main query. This is what makes recursion beautiful, but then there is also no CTE (or other query in any SQL engine) that can recurse over multiple states of data (i.e. query data in one single query to reflect results from both before and after a delete in the source table), nor can a CTE be updated or deleted from, its data must persist atomically (with some exceptions when using non-deterministic functions, like random).

These are not so much "inabilities" of SQL engines, but more due to explicit SQL and set-algebra rules.

So this is not possible in a single query.
This is possible in a single query, though not directly. CTE are very flexible. CTE can be used to write your own DBMS, which is able to store tables and make a recursive DELETE. Then: (1) copy ``points'' table to SQL-one-query-written DBMS; (2) do a recursive DELETE; (3) output a final table to a proper DBMS -- SQLite.

As Simon has written, the problem can be presented as an image processing problem. Further, image is a 2D Cartesian product of X/Y coordinates with points being relations between Xs and Ys -- a classic relational problem. So, at the beginning, my solution tries to produce two tables: xaxis/yaxis from a n-to-n relation ``points''. Each ``*axis'' table contains id (coordinate) of a row/column and a number of points lying on it. Next, let's remove rows/columns having a number of points less then given values until all remaining rows/columns (even if 0, at the border case) have enough points. Parameters (nX, nY) are passed by ``params'' CTE:

Let's generate 100 random points:

CREATE TABLE points AS WITH cte(x,y,n) AS (SELECT (random() % 10 + 10) % 10 + 1, (random() % 10 + 10) % 10 + 1, 1 UNION ALL SELECT (random() % 10 + 10) % 10 + 1, (random() % 10 + 10) % 10 + 1, n + 1 FROM cte WHERE n < 100) SELECT x, y FROM cte;

The preceding statement creates table ``points(x, y)'' containing 100 points with coordinates <1;10> -- points can be repeated -- it does not matter.
Now, let's SELECT x, y FROM points WHERE nX = 6 AND nY = 8:

======
WITH
  params(nx, ny) AS (SELECT 6, 8),
  main(elem, rest) AS (
    SELECT NULL, (
      WITH
        state(clock, points, xaxis, yaxis, nxmin, nxmax, nymin, nymax) AS (
          SELECT
            0,
            (SELECT GROUP_CONCAT(x || ' ' || y) FROM points),
(SELECT GROUP_CONCAT(x || ' ' || n) FROM (SELECT x, COUNT(x) n FROM points GROUP BY x)), (SELECT GROUP_CONCAT(y || ' ' || n) FROM (SELECT y, COUNT(y) n FROM points GROUP BY y)), (SELECT MIN(n) FROM (SELECT x, COUNT(x) n FROM points GROUP BY x)), (SELECT MAX(n) FROM (SELECT x, COUNT(x) n FROM points GROUP BY x)), (SELECT MIN(n) FROM (SELECT y, COUNT(y) n FROM points GROUP BY y)), (SELECT MAX(n) FROM (SELECT y, COUNT(y) n FROM points GROUP BY y))
          UNION ALL
          SELECT
            (clock + 1) % 3,
            CASE clock WHEN 0 THEN
            (SELECT GROUP_CONCAT(x || ' ' || y) FROM (
              WITH
                cte(elem, rest) AS (
                  SELECT NULL, state.points
                  UNION ALL
                  SELECT
SUBSTR(rest, 1, IFNULL(NULLIF(INSTR(rest, ','), 0) - 1, LENGTH(rest))),
                    SUBSTR(rest, NULLIF(INSTR(rest, ','), 0) + 1)
                  FROM cte
                  WHERE rest IS NOT NULL
                )
              SELECT
                CAST(elem AS INTEGER) x,
                CAST(SUBSTR(elem, INSTR(elem, ' ')) AS INTEGER) y
                FROM cte WHERE elem IS NOT NULL
            )
            WHERE (x NOT IN (
              SELECT x FROM (
                WITH
                  cte(elem, rest) AS (
                    SELECT NULL, state.xaxis
                    UNION ALL
                    SELECT
SUBSTR(rest, 1, IFNULL(NULLIF(INSTR(rest, ','), 0) - 1, LENGTH(rest))),
                      SUBSTR(rest, NULLIF(INSTR(rest, ','), 0) + 1)
                    FROM cte
                    WHERE rest IS NOT NULL
                  )
                SELECT
                  CAST(elem AS INTEGER) x,
                  CAST(SUBSTR(elem, INSTR(elem, ' ')) AS INTEGER) n
                  FROM cte WHERE elem IS NOT NULL
              ) WHERE n < (SELECT nx FROM params)
            )) AND (y NOT IN (
              SELECT y FROM (
                WITH
                  cte(elem, rest) AS (
                    SELECT NULL, state.yaxis
                    UNION ALL
                    SELECT
SUBSTR(rest, 1, IFNULL(NULLIF(INSTR(rest, ','), 0) - 1, LENGTH(rest))),
                      SUBSTR(rest, NULLIF(INSTR(rest, ','), 0) + 1)
                    FROM cte
                    WHERE rest IS NOT NULL
                  )
                SELECT
                  CAST(elem AS INTEGER) y,
                  CAST(SUBSTR(elem, INSTR(elem, ' ')) AS INTEGER) n
                  FROM cte WHERE elem IS NOT NULL
              ) WHERE n < (SELECT ny FROM params)
            ))) ELSE points END,
            CASE clock WHEN 1 THEN
(SELECT GROUP_CONCAT(x || ' ' || n) FROM (SELECT x, COUNT(x) n FROM (
              WITH
                cte(elem, rest) AS (
                  SELECT NULL, state.points
                  UNION ALL
                  SELECT
SUBSTR(rest, 1, IFNULL(NULLIF(INSTR(rest, ','), 0) - 1, LENGTH(rest))),
                    SUBSTR(rest, NULLIF(INSTR(rest, ','), 0) + 1)
                  FROM cte
                  WHERE rest IS NOT NULL
                )
              SELECT
                CAST(elem AS INTEGER) x
                FROM cte WHERE elem IS NOT NULL
            ) GROUP BY x)) ELSE xaxis END,
            CASE clock WHEN 1 THEN
(SELECT GROUP_CONCAT(y || ' ' || n) FROM (SELECT y, COUNT(y) n FROM (
              WITH
                cte(elem, rest) AS (
                  SELECT NULL, state.points
                  UNION ALL
                  SELECT
SUBSTR(rest, 1, IFNULL(NULLIF(INSTR(rest, ','), 0) - 1, LENGTH(rest))),
                    SUBSTR(rest, NULLIF(INSTR(rest, ','), 0) + 1)
                  FROM cte
                  WHERE rest IS NOT NULL
                )
              SELECT
                CAST(SUBSTR(elem, INSTR(elem, ' ')) AS INTEGER) y
                FROM cte WHERE elem IS NOT NULL
            ) GROUP BY y)) ELSE yaxis END,
            CASE clock WHEN 2 THEN
            (SELECT MIN(n) FROM (
              WITH
                cte(elem, rest) AS (
                  SELECT NULL, state.xaxis
                  UNION ALL
                  SELECT
SUBSTR(rest, 1, IFNULL(NULLIF(INSTR(rest, ','), 0) - 1, LENGTH(rest))),
                    SUBSTR(rest, NULLIF(INSTR(rest, ','), 0) + 1)
                  FROM cte
                  WHERE rest IS NOT NULL
                )
              SELECT
                CAST(SUBSTR(elem, INSTR(elem, ' ')) AS INTEGER) n
                FROM cte WHERE elem IS NOT NULL
            )) ELSE nxmin END,
            CASE clock WHEN 2 THEN
            (SELECT MAX(n) FROM (
              WITH
                cte(elem, rest) AS (
                  SELECT NULL, state.xaxis
                  UNION ALL
                  SELECT
SUBSTR(rest, 1, IFNULL(NULLIF(INSTR(rest, ','), 0) - 1, LENGTH(rest))),
                    SUBSTR(rest, NULLIF(INSTR(rest, ','), 0) + 1)
                  FROM cte
                  WHERE rest IS NOT NULL
                )
              SELECT
                CAST(SUBSTR(elem, INSTR(elem, ' ')) AS INTEGER) n
                FROM cte WHERE elem IS NOT NULL
            )) ELSE nxmax END,
            CASE clock WHEN 2 THEN
            (SELECT MIN(n) FROM (
              WITH
                cte(elem, rest) AS (
                  SELECT NULL, state.yaxis
                  UNION ALL
                  SELECT
SUBSTR(rest, 1, IFNULL(NULLIF(INSTR(rest, ','), 0) - 1, LENGTH(rest))),
                    SUBSTR(rest, NULLIF(INSTR(rest, ','), 0) + 1)
                  FROM cte
                  WHERE rest IS NOT NULL
                )
              SELECT
                CAST(SUBSTR(elem, INSTR(elem, ' ')) AS INTEGER) n
                FROM cte WHERE elem IS NOT NULL
            )) ELSE nymin END,
            CASE clock WHEN 2 THEN
            (SELECT MAX(n) FROM (
              WITH
                cte(elem, rest) AS (
                  SELECT NULL, state.yaxis
                  UNION ALL
                  SELECT
SUBSTR(rest, 1, IFNULL(NULLIF(INSTR(rest, ','), 0) - 1, LENGTH(rest))),
                    SUBSTR(rest, NULLIF(INSTR(rest, ','), 0) + 1)
                  FROM cte
                  WHERE rest IS NOT NULL
                )
              SELECT
                CAST(SUBSTR(elem, INSTR(elem, ' ')) AS INTEGER) n
                FROM cte WHERE elem IS NOT NULL
            )) ELSE nymax END
          FROM state
          WHERE (SELECT nx FROM params) BETWEEN nxmin + 1 AND nxmax OR
                (SELECT ny FROM params) BETWEEN nymin + 1 AND nymax
) SELECT points FROM state WHERE nxmin >= (SELECT nx FROM params) AND nymin >= (SELECT ny FROM params)
    )
    UNION ALL
    SELECT
SUBSTR(rest, 1, IFNULL(NULLIF(INSTR(rest, ','), 0) - 1, LENGTH(rest))),
      SUBSTR(rest, NULLIF(INSTR(rest, ','), 0) + 1)
    FROM main
    WHERE rest IS NOT NULL
  )
SELECT
  CAST(elem AS INTEGER) x,
  CAST(SUBSTR(elem, INSTR(elem, ' ')) AS INTEGER) y
  FROM main WHERE elem IS NOT NULL;
======

If you have received an empty set then you will have to decrease nX/nY, append more points or generate new ``points'' table.

... just for fun.

-- best regards

Cezary H. Noweta
_______________________________________________
sqlite-users mailing list
sqlite-users@mailinglists.sqlite.org
http://mailinglists.sqlite.org/cgi-bin/mailman/listinfo/sqlite-users

Reply via email to