PostgreSQL v9.X have real "array of record"? - arrays

This query works fine,
WITH test AS (
SELECT array_agg(t) as x FROM (
SELECT 1111 as id, 'aaaaa' as cc
) AS t
) SELECT x[1] FROM test;
but, can I access the recod elements? I try SELECT x[1].id; SELECT x[1][1]; ... nothing works.
PS: with Google we see only OLD solutions... The context here is v9.X, no news about "array of record"?
I try also
select x[1] from (select array[row(1,2)] as x) as t;
no solution to access only item 1 or only item 2.
A clue that I was unable to follow: postgresql.1045698.n5.nabble.com solve the problem using a CREATE TYPE... Ok, but I need "all in a query" solution. Where the "dynamic typing" of PostgreSQL?? How to CAST or express the type without a CREATE TYPE clause?

At present there does not appear to be any syntax for accessing a record of anonymous type except via the function call syntax or via hstore. That's unfortunate, but not likely to get fixed in a hurry unless someone who really cares comes along to make it happen. There are other priorities.
You have three workaround options:
CREATE TYPE
hstore
CREATE TYPE
The issue is with records of anonymous type. So make it not anonymous. Unfortunately this is only possible before it becomes an anonymous record type; you can't currently cast from record to a usertype. So you'd need to do:
CREATE TYPE some_t AS (id integer, cc text);
WITH test AS (
SELECT array_agg(t::some_t) as x FROM (
SELECT 1111 as id, 'aaaaa' as cc
) AS t
) SELECT x[1].id FROM test;
Note the cast of the subquery output to some_t before aggregation.
I can't say I understand why this cast can't be performed after indexing the array instead.
hstore
As usual, hstore rides to the mostly-rescue with difficult type problems.
regress=> WITH test AS (
SELECT array_agg(t) as x FROM (
SELECT 1111 as id, 'aaaaa' as cc
) AS t
) SELECT hstore(x[1])->'id' FROM test;
?column?
----------
1111
(1 row)
You need the hstore extension, and I'm sure it's not efficient, but it works. This builds on the hstore support for creating a hstore from anonymous records that was added to support NEW and OLD in triggers, a past pain-point.
Wrapper function?
Turns out you can't get around it with a simple wrapper function to let you specify the type on the call-site:
regress=> CREATE OR REPLACE FUNCTION identity(record) RETURNS record AS $$
SELECT $1;
$$ LANGUAGE sql IMMUTABLE;
ERROR: SQL functions cannot have arguments of type record
so you'd have to use a higher-overhead procedural language, at which point you might as well use hstore instead, it'll be faster and easier.
Making it better?
So, this is all a bit ugly. It's not ever likely to be possible to directly index a field from an anonymous record, since it might not exist and its type cannot be deduced. But there's no reason we can't use the type system feature that allows us to return record from a function and specify its type on the caller-side to also do so in casts.
It should be possible to make PostgreSQL support something like:
WITH test AS (
SELECT array_agg(t) as x FROM (
SELECT 1111 as id, 'aaaaa' as cc
) AS t
) SELECT (x[1] AS some_t(id integer, cc text)).id FROM test;
it'd just involve appropriate parser-hacking, and a way to make sure that was never ambiguously parsed in conflict with a column-alias.
Really, even type-inference could be possible if someone was willing to put the work in and convince the team that the rather large amount of query-planner processor time required was worth it. (unlikely).
This is an irritating, but minor, corner in the type system. If you want it to change you will need to make noise on pgsql-general, and accompany that noise with the willingness to do real work to improve the problem. This may involve learning more than you ever wanted to know about the innards of PostgreSQL's type system, learning the fun of "backward compatibility", and having frustrating arguments around and around in circles. Welcome to open source!

Related

How do I select for a row using multiple clauses in PACT smart contract language

The PACT documentation clearly states how to select for a single condition in the where clause, but it is not so clear on how to select for multiple clauses which seems much more general and important for real world use cases than a single clause example.
Pact-lang select row function link
For instance I was trying to select a set of dice throws across the room name and the current round.
(select 'throws (where (and ('room "somename") ('round 2)))
But this guy didn't resolve and the error was not so clear. How do I select across multiple conditions in the select function?
The first thing we tried was to simply select via a single clause which returns a list:
(select 'throws (where (and ('room "somename")))
A: [object{throw-schema},object{throw-schema}]
And then we applied the list operator "filter" to the result:
(filter (= 'with-read-function' 1) (select 'throws (where (and ('room "somename"))))
Please keep in mind we had a further function that read the round and spit back the round number and we filtered for equality of the round value.
This ended up working but it felt very janky.
The second thing we tried was to play around with the and syntax and we eventually found a nice way to express it although not quite so intuitive as we would have liked. It just took a little elbow grease.
The general syntax is:
(select 'throws (and? (where condition1...) (where condition2...))
In this case the and clause was lazy, hence the ? operator. We didn't think that we would have to declare where twice, but its much cleaner than the filter method we first tried.
The third thing we tried via direction from the Kadena team was a function we had yet to purview: Fold DB.
(let* ((qry (lambda (k obj) true)) ;;
(f (lambda(x) [(at 'firstName x), (at 'b x)])) ) (fold-db people (qry) (f)) )
This actually is the most correct answer but it was not obvious from the initial scan and would be near inscrutable for a new user to put together with no pact experience.
We suggest a simple sentence ->
"For multiple conditions use fold-db function."
In the documentation.
This fooled us because we are so used to using SQL syntax that we didn't imagine that there was a nice function like this lying around and we got stuck in our ways trying to figure out conditional logic.

MS Access, use query name as field default value

My department uses a software tool that can use a custom component library sourced from Tables or Queries in an MS Access database.
Table: Components
ID: AutoNumber
Type: String
Mfg: String
P/N: String
...
Query: Resistors
SELECT Components.*
FROM Components
WHERE Components.Type = "Resistors"
Query: Capacitors
SELECT Components.*
FROM Components
WHERE Components.Type = "Capacitors"
These queries work fine for SELECT. But when users add a row to the query, how can I ensure the correct value is saved to the Type field?
Edit #2:
Nope, can't be done. Sorry.
Edit #1:
As was pointed out, I may have misunderstood the question. It's not a wonky question after all, but perhaps an easy one?
If you're asking how to add records to your table while making sure that, for example, "the record shows up in a Resistors query if it's a Resistor", then it's a regular append query, that specifies Resisitors as your Type.
For example:
INSERT INTO Components ( ID, Type, Mfg )
SELECT 123, 'Resistors', 'Company XYZ'
If you've already tried that and are having problems, it could be because you are using a Reserved Word as a field name which, although it may work sometimes, can cause problems in unexpected ways.
Type is a word that Access, SQL and VBA all use for a specific purpose. It's the same idea as if you used SELECT and FROM as field or table names. (SELECT SELECT FROM FROM).
Here is a list of reserved words that should generally be avoided. (I realize it's labelled Access 2007 but the list is very similar, and it's surprisingly difficult to find an recent 'official' list for Excel VBA.)
Original Answer:
That's kind a a wonky way to do things. The point of databases is to organize in such a way as to prevent duplication of not only data, but queries and codes as well
I made up the programming rule for my own use "If you're doing anything more than once, you're doing it wrong." (That's not true in all cases but a general rule of thumb nonetheless.)
Are the only options "Resistors" and "Capacitors"? (...I hope you're not tracking the inventory of an electronics supply store...) If there are may options, that's even more reason to find an alternative method.
To answer your question, in the Query Design window, it is not possible to return the name of the open query.
Some alternative options:
As #Erik suggested, constrain to a control on a form. Perhaps have a drop-down or option buttons which the user can select the relevant type. Then your query would look like:
SELECT * FROM Components WHERE Type = 'Forms![YourFormName]![NameOfYourControl]'
In VBA, have the query refer to the value of a variable, foe example:
Dim TypeToDel as String
TypeToDel = "Resistor"
DoCmd.RunSQL "SELECT * FROM Components WHERE Type = '" & typeToDel'"
Not recommended, but you could have the user manually enter the criteria. If your query is like this:
SELECT * FROM Components WHERE Type = '[Enter the component type]'
...then each time the query is run, it will prompt:
Similarly, you could have the query prompt for an option, perhaps a single-digit or a code, and have the query choose the the appropriate criteria:
...and have an IF statement in the query criteria.
SELECT *
FROM Components
WHERE Type = IIf([Enter 1 for Resistors, 2 for Capacitors, 3 for sharks with frickin' laser beams attached to their heads]=1,'Resistors',IIf([Enter 1 for Resistors, 2 for Capacitors, 3 for sharks with frickin' laser beams attached to their heads]=2,'Capacitors','LaserSharks'));
Note that if you're going to have more than 2 options, you'll need to have the parameter box more than once, and they must be spelled identically.
Lastly, if you're still going to take the route of a separate query for each component type, as long as you're making separate queries anyway, why not just put a static value in each one (just like your example):
SELECT * FROM Components WHERE Type = 'Resistor'
There's another wonky answer here but that's just creating even more duplicate information (and more future mistakes).
Side note: Type is a reserved word in Access & VBA; you might be best to choose another. (I usually prefix with a related letter like cType.)
More Information:
Use parameters in queries, forms, and reports
Use parameters to ask for input when running a query
Microsoft Access Tips & Tricks: Parameter Queries
 • Frickin' Lasers

SQL Server function/stored proc to return a set of aggregations based on a set of points

I have a table of x,y coordinates - let's say they represent map locations of cell phone pings. The table also has a personID to represent the owner of the phone.
I also have a function (which could be changed to something else to suit this solution) that accepts a set of points as a table variable and generates some aggregations of them (average and stdev in both directions). The example shows the table type and a function that could do this.
create type tblCoordinate AS table (x float, y float)
go
create function PointsAggregations(#pts tblCoordinate readonly)
returns table as
return
select avg(x) AvgX, avg(y) avgY, stdev(x) stdevx, stdev(y) stdevy
from #pts
Question is how would I structure this so I could get a table of these aggregations by PersonID. That is, I would like to call this function and pass it the unique set of points for every PersonID in the table, and end up with a table of structure.
PersonID, avgX, avgY, stdevX, stdevY.
Feels like a weird reverse-cross apply kinda thing, but I can't quite figure out if it's possible. Note: the actual problem is much more complicated than the simple aggregations shown here - I am trying to encapsulate an algorithm that creates a "confidence ellipse" of the points - lots of math involved that aren't applicable to the posted question - so simply performing the aggregations within a single query and getting rid of the function isn't a solution.

SQL Injection-safe call of polymorphic function

Several times I've found myself refactoring web application code and end up wanting to do something like this (Groovy in this case, but could be anything):
Map getData(String relationName, Integer rowId) {
def sql = Sql.newInstance([...])
def result = sql.firstRow('SELECT getRelationRow(?,?)', relationName, rowId)
sql.close()
return new HashMap(result)
}
where the stored procedure getRelationRow(relname text, rowid integer) executes dynamic sql to retrieve the row of the specified rowid in the requested relation. The best example I've seen of such a function is this polymorphic function using anyelement type, and is called as
SELECT * FROM data_of(NULL::pcdmet, 17);
However to call this in the above code would require
def result = sql.firstRow("SELECT * FROM data_of(NULL::${relationName},?)", rowId)
that is, it required the relation name to be pasted into the query, which risks SQL Injection. So is there away to keep the polymorphic goodness of the stored procedure but allow it to be called with generic relation names?
I don't think it can be done this way. I assume Groovy is using prepared statements here, which requires that input and return types are known at prepare time, while my function derives the return type from the polymorphic input type.
I am pretty sure you need string concatenation. But don't fret, there are functions like pg_escape() to sanitize table names and make SQLi impossible. Don't know Groovy, but it should have that, too.
Or does it?
Based on the function data_of(..) at the end of this related answer:
Refactor a PL/pgSQL function to return the output of various SELECT queries
With PREPARE I can make this work by declaring the return type explicitly:
PREPARE fooplan ("relationName") AS -- table name works as row type
SELECT * FROM data_of($1, $2);
Then I can hand in NULL, which is cast to "relationName" from the prepared context:
EXECUTE fooplan(NULL, 1);
So this might work after all, if your interface supports this. But you still have to concatenate the table name as return data type (and therefore defend against SQLi). Catch 22 I guess.

Why Is My Inline Table UDF so much slower when I use variable parameters rather than constant parameters?

I have a table-valued, inline UDF. I want to filter the results of that UDF to get one particular value. When I specify the filter using a constant parameter, everything is great and performance is almost instantaneous. When I specify the filter using a variable parameter, it takes a significantly larger chunk of time, on the order of 500x more logical reads and 20x greater duration.
The execution plan shows that in the variable parameter case the filter is not applied until very late in the process, causing multiple index scans rather than the seeks that are performed in the constant case.
I guess my questions are: Why, since I'm specifying a single filter parameter that is going to be highly selective against an indexed field, does my performance go into the weeds when that parameter is in a variable? Is there anything I can do about this?
Does it have something to do with the analytic function in the query?
Here are my queries:
CREATE FUNCTION fn_test()
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN
SELECT DISTINCT GCN_SEQNO, Drug_package_version_ID
FROM
(
SELECT COALESCE(ndctbla.GCN_SEQNO, ndctblb.GCN_SEQNO) AS GCN_SEQNO,
dpv.Drug_package_version_ID, ROW_NUMBER() OVER (PARTITION BY dpv.Drug_package_version_id ORDER BY
ndctbla.GCN_SEQNO DESC) AS Predicate
FROM dbo.Drug_Package_Version dpv
LEFT JOIN dbo.NDC ndctbla ON ndctbla.NDC = dpv.Sp_package_code
LEFT JOIN dbo.NDC ndctblb ON ndctblb.SPC_NDC = dpv.Sp_package_code
) iq
WHERE Predicate = 1
GO
GRANT SELECT ON fn_test TO public
GO
-- very fast
SELECT GCN_SEQNO
FROM dbo.fn_test()
WHERE Drug_package_version_id = 10000
GO
-- comparatively slow
DECLARE #dpvid int
SET #dpvid = 10000
SELECT GCN_SEQNO
FROM dbo.fn_test()
WHERE Drug_package_version_id = #dpvid
Once you create a new projection through a UDF, it can't be expected that your indexes will still apply on the columns that are indexed on the original table and included in the projection. When you filter on the projection (and not in the UDF against the original table with the indexes) the indexes no longer apply.
What you want to do is parameterize the function to take in the parameter.
If you find that you have too many fields that you want to set parameters on, then you might want to take a look at indexed views, as you can create your projection and index it as well and then run queries against that.
Simply, the constant is easy to evaluate in the plan. The local variable is not. Especially with the ranking function and filter Predicate = 1
Paraphrasing casparOne, you need to push the filter as far inwards as possible so that you filter on dpv.Drug_package_version_id inside the iq derived table.
If you do that, then you also have no need for the PARTITION BY because you have only a single dpv.Drug_package_version_id. Then you can do a cleaner ...TOP 1 ... ORDER BY ndctbla.GCN_SEQNO DESC.
The responses I got were good, and I learned from them, but I think I've found an answer that satisfies me.
I do think it's the use of the PARTITION BY clause that is causing the problem here. I reformulated the UDF using a variant of the self-join idiom:
SELECT t1.A, t1.B, t1.C
FROM T t1
INNER JOIN
(
SELECT A, MAX(C) AS C
FROM T
GROUP BY A
) t2 ON t1.A = t2.A AND t1.C = t2.C
Ironically, this is more performant than using the SQL 2008-specific query, and also the optimizer doesn't have a problem with joining this version of the query using variables rather than constants. At this point, I'm concluding that the optimizer just doesn't handle the more recent SQL extensions as well as the older stuff. As a bonus, I can make use of the UDF now, in my pre-upgraded SQL 2000 platforms.
Thanks for your help, everyone!

Resources