X-Original-To: pgsql-sql-postgresql.org@localhost.postgresql.org Received: from localhost (unknown [200.46.204.144]) by svr1.postgresql.org (Postfix) with ESMTP id B019B329C7B for ; Mon, 13 Sep 2004 16:03:46 +0100 (BST) Received: from svr1.postgresql.org ([200.46.204.71]) by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) with ESMTP id 46973-03 for ; Mon, 13 Sep 2004 15:03:42 +0000 (GMT) Received: from 10.hostcorporate.com (unknown [69.41.231.186]) by svr1.postgresql.org (Postfix) with SMTP id 9DE54329C70 for ; Mon, 13 Sep 2004 16:03:40 +0100 (BST) Received: (qmail 736 invoked from network); 13 Sep 2004 14:51:45 -0000 Received: from localhost (HELO localhost.localdomain) (127.0.0.1) by localhost.10.hostcorporate.com with SMTP; 13 Sep 2004 14:51:45 -0000 Message-ID: <4145B6C4.1080605@zara.6.isreserved.com> Date: Mon, 13 Sep 2004 22:03:32 +0700 From: David Garamond User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7b) Gecko/20040421 X-Accept-Language: en-us, en MIME-Version: 1.0 To: pgsql-sql@postgresql.org Subject: Interpolation and extrapolation in SQL Content-Type: text/plain; charset=us-ascii; format=flowed Content-Transfer-Encoding: 7bit X-Virus-Scanned: by amavisd-new at hub.org X-Spam-Status: No, hits=0.3 tagged_above=0.0 required=5.0 tests=UPPERCASE_25_50 X-Spam-Level: X-Archive-Number: 200409/95 X-Sequence-Number: 18941 On my first try, interpolation and extrapolation turns out to be pretty easy to do. In psql: -- the "lookup" table CREATE TABLE p ( x DOUBLE PRECISION NOT NULL UNIQUE, y DOUBLE PRECISION NOT NULL ); INSERT INTO p VALUES (1,1); INSERT INTO p VALUES (2,5); INSERT INTO p VALUES (5,14); INSERT INTO p VALUES (10,21); -- the table that contains our x values, the y values of which will be -- looked up in the lookup table. CREATE TABLE q ( x DOUBLE PRECISION NOT NULL ); INSERT INTO q VALUES (0); INSERT INTO q VALUES (1); INSERT INTO q VALUES (2); INSERT INTO q VALUES (3.5); INSERT INTO q VALUES (5.5); INSERT INTO q VALUES (10); INSERT INTO q VALUES (11); -- query A. only handles interpolation \set x1 '(SELECT p.x FROM p WHERE p.x <= q.x ORDER BY p.x DESC LIMIT 1)' \set x2 '(SELECT p.x FROM p WHERE p.x >= q.x ORDER BY p.x ASC LIMIT 1)' \set y1 '(SELECT p.y FROM p WHERE p.x <= q.x ORDER BY p.x DESC LIMIT 1)' \set y2 '(SELECT p.y FROM p WHERE p.x >= q.x ORDER BY p.x ASC LIMIT 1)' SELECT q.x, CASE WHEN :x1 = :x2 THEN :y1 ELSE (:y1 + (q.x-:x1)/(:x2-:x1)*(:y2-:y1)) END AS y FROM q; -- query B. also handles extrapolation, and the note column tells us -- whether a certain y value is directly taken from p, or interpolated, -- or extrapolated. \set x0 '(SELECT p.x FROM p WHERE p.x <= q.x ORDER BY p.x DESC LIMIT 1 OFFSET 1)' \set x3 '(SELECT p.x FROM p WHERE p.x >= q.x ORDER BY p.x ASC LIMIT 1 OFFSET 1)' \set y0 '(SELECT p.y FROM p WHERE p.x <= q.x ORDER BY p.x DESC LIMIT 1 OFFSET 1)' \set y3 '(SELECT p.y FROM p WHERE p.x >= q.x ORDER BY p.x ASC LIMIT 1 OFFSET 1)' SELECT q.x, CASE WHEN :x1 = :x2 THEN :y1 WHEN :x1 IS NULL THEN (:y2 + (q.x-:x2)/(:x3-:x2)*(:y3-:y2)) WHEN :x2 IS NULL THEN (:y0 + (q.x-:x0)/(:x1-:x0)*(:y1-:y0)) ELSE (:y1 + (q.x-:x1)/(:x2-:x1)*(:y2-:y1)) END AS y, CASE WHEN :x1 = :x2 THEN 'direct' WHEN :x1 IS NULL THEN 'extrapolated to the left' WHEN :x2 IS NULL THEN 'extrapolated to the right' ELSE 'interpolated' END AS note FROM q; -- C. to handle the case where x in p is not unique, replace the x0..x3 and y0..y3 template into: \set x1 '(SELECT p2.x FROM (SELECT DISTINCT x FROM p) p2 WHERE p2.x <= q.x ORDER BY p2.x DESC LIMIT 1)' \set x2 '(SELECT p2.x FROM (SELECT DISTINCT x FROM p) p2 WHERE p2.x >= q.x ORDER BY p2.x ASC LIMIT 1)' \set y1 '(SELECT p.y FROM (SELECT DISTINCT x FROM p) p2 LEFT JOIN p ON p.x=p2.x WHERE p2.x <= q.x ORDER BY p2.x DESC LIMIT 1)' \set y2 '(SELECT p.y FROM (SELECT DISTINCT x FROM p) p2 LEFT JOIN p ON p.x=p2.x WHERE p2.x >= q.x ORDER BY p2.x ASC LIMIT 1)' \set x0 '(SELECT p2.x FROM (SELECT DISTINCT x FROM p) p2 WHERE p2.x <= q.x ORDER BY p2.x DESC LIMIT 1 OFFSET 1)' \set x3 '(SELECT p2.x FROM (SELECT DISTINCT x FROM p) p2 WHERE p2.x >= q.x ORDER BY p2.x ASC LIMIT 1 OFFSET 1)' \set y0 '(SELECT p.y FROM (SELECT DISTINCT x FROM p) p2 LEFT JOIN p ON p.x=p2.x WHERE p2.x <= q.x ORDER BY p2.x DESC LIMIT 1 OFFSET 1)' \set y3 '(SELECT p.y FROM (SELECT DISTINCT x FROM p) p2 LEFT JOIN p ON p.x=p2.x WHERE p2.x >= q.x ORDER BY p2.x ASC LIMIT 1 OFFSET 1)' Questions: 1) Is the above correct? (It gives me correct result, but I'm not 100% sure) 2) Does the optimizer cache the result of identical subqueries (e.g. :x1 or :x2, which is mentioned several times in the query)? If yes, how do I know this? 3) Is there another (simpler, more elegant, more efficient) way to do interpolation/extrapolation in SQL? -- dave