Background
Creating a wrapper function for a SQL statement.
Problem
A function returns 1 row, whereas the query upon which the function is based returns 100+ rows. The parameter values are identical:
SELECT * FROM as_rpt.tasc_fsa( 'ABC', '2010-01-01'::date, '2011-01-01'::date );
The tasc_fsa function is a SELECT statement with a few table joins; the function language is 'sql' rather than 'plpgsql'.
Question
What reasons would a function return a single row yet the exact same query that the function uses, when not called via the function, correctly returns over 100 rows?
Any ideas would be most helpful.
Thank you!
Have you use RETURN SETOF ...?
SETOF indicates you want to return more than 1 row.
I'm pretty sure you forgot to use 'RETURN SETOF'.
You mention the function "joins a few tables". Your example obviously doesn't. If there are joins being performed, it is possible this is causing less results.
The way the dates are being interpreted is another possibility. When you get 1 answer, is it within the range you specify?
Related
I have a curious question that's going on my mind while looking at my datatables.
I noticed that some columns of my table has no values, like just the column only.
Is it possible when you want to call that column it will return a value of 0?
I tried to call it with a simple SELECT query in sql but its giving me a notice.
Notice: Trying to get property of non-object MySQL result on line (number line)
Can this be used with an if-else statement from the query or a case statement from the query? Hoping for your opinions on this. Thank you :)
Try to use some function like NVL() in Oracle, or IFNULL() in MySQL. This functions set a value when you got a NULL value.
I have a stored procedure in MySQL that takes a single parameter that can consist of 1 or more values. I want to be able to filter by these values, so I created a function that can take a string and a delimiter parameter
my_func('val1,val2,val3',',')
and it returns a string
'val1','val2','val3'
. I am then calling this function in the where clause such as
WHERE x IN (my_func('val1,val2,val3',','))
But this is not working. It does not give any error, but it keeps running without ever returning anything. I have tested the function individually and it works fine and returns in less than a second. The query I am trying to run it in is a test query that is very basic. Replacing the function with a regular string such as
WHERE x IN ('val1','val2','val3')
works perfectly fine and returns in just a couple seconds. Is what I am trying to do even possible? Thanks!
IN requires the argument to be a literal list, it doesn't re-parse the string. Use FIND_IN_SET:
WHERE FIND_IN_SET(x, 'val1,val2,val3'))
I'm using Postgres 9.2 to generate some JSON data. For each nested table I'm doing this nested set of functions:
SELECT array_to_json(
coalesce(
array_agg(
row_to_json(foo)),
ARRAY[]::json[])
)
FROM foo
The effect is to create a json array with each row being the json collection for the row. The coalesce ensures that I get an empty array rather than nil if the table is empty. In most cases foo is actually a subquery but I don't think that is relevent to the question.
I want to create a function table_to_json_array(expression) such that this has the same effect as above:
SELECT table_to_json_array(foo) FROM foo
I need to use this lots so I was planning to create a Postgres function to have the effect of the combination of these calls to clean up my queries. Looking at the documentation it seems as if I need to create an aggregate rather than a function to take a table argument but those look like I would need to reimplement array_agg myself.
Have I missed something (possibly just the type a function would need to take)? Any suggestions?
In most cases foo is actually a subquery but I don't think that is
relevent to the question.
Unfortunately, it is. You can create a function with regclass argument:
create or replace function table_to_json(source regclass)
returns json language plpgsql
as $$
declare
t json;
begin
execute format ('
SELECT
array_to_json(
coalesce(array_agg(row_to_json(%s)),
ARRAY[]::json[]))
FROM %s', source, source)
into t;
return t;
end $$;
select table_to_json('my_table');
select table_to_json('my_schema.my_view');
But in context:
select table_to_json_rec(arg)
from (select * from my_table) arg
the argument arg is of type record. PL/pgSQL functions cannot accept type record. The only way to get this is a C function, what I guess is not an option. The same goes for aggregates (you must have a function to define an aggregate).
Postgres 9.3 adds a json_agg function which simplifies the specific query I need although this isn't a general solution to the aggregate functions issue. It still needs a coalesce function to ensure the empty set is properly returned.
SELECT coalesce( json_agg(foo), json'[]')
FROM foo
And it works even when foo is a subquery.
This will sound silly, but trust me it is for a good (i.e. over-engineered) cause.
Is it possible to write a SQL query using an IN clause which selects everything in that table without knowing anything about the table? Keep in mind this would mean you can't use a subquery that references the table.
In other words I would like to find a statement to replace "SOMETHING" in the following query:
SELECT * FROM table_a WHERE table_a.id IN (SOMETHING)
so that the results are identical to:
SELECT * FROM table_a
by doing nothing beyond changing the value of "SOMETHING"
To satisfy the curious I'll share the reason for the question.
1) I have a FactoryObject abstract class which grants all models that extend it some glorious factory method magic using two template methods: getData() and load()
2) Models must implement the template methods. getData is a static method that accepts ID constraints, pulls rows from the database, and returns a set of associative arrays. load is not static, accepts an associative array, and populates the object based on that array.
3) The non-abstract part of FactoryObject implements a getObject() and a getObjects() method. These call getData, create objects, and loads() the array responses from getData to create and return populated objects.
getObjects() requires ID constraints as an input, either in the form of a list or in the form of a subquery, which are then passed to getData(). I wanted to make it possible to pass in no ID constraints to get all objects.
The problem is that only the models know about their tables. getObjects() is implemented at a higher level and so it doesn't know what to pass getData(), unless there was a universal "return everything" clause for IN.
There are other solutions. I can modify the API to require getData to accept a special parameter and return everything, or I can implement a static getAll[ModelName]s() method at the model level which calls:
static function getAllModelObjects() {
return getObjects("select [model].id from [model]");
}
This is reasonable and may fit the architecture anyway, but I was curious so I thought I would ask!
Works on SQL Server:
SELECT * FROM table_a WHERE table_a.id IN (table_a.id)
Okay, I hate saying no so I had to come up with another solution for you.
Since mysql is opensource you can get the source and incorporate a new feature that understands the infinity symbol. Then you just need to get the mysql community to buy into the usefulness of this feature (steer the conversation away from security as much as possible in your attempts to do so), and then get your company to upgrade their dbms to the new version once this feature has been implemented.
Problem solved.
The answer is simple. The workaround is to add some criteria like these:
# to query on a number column
AND (-1 in (-1) OR sample_table.sample_column in (-1))
# or to query on a string column
AND ('%' in ('%') OR sample_table.sample_column in ('%'))
Therefore, in your example, two following queries should return the same result as soon as you pass -1 as the parameter value.
SELECT * FROM table_a;
SELECT * FROM table_a WHERE (-1 in (-1) OR table_a.id in (-1));
And whenever you want to filter something out, you can pass it as a parameter. For example, in the following query, the records with id of 1, 2 and 6 are filtered.
SELECT * FROM table_a WHERE (-1 in (1, 2, 6) OR table_a.id in (1, 2, 6));
In this case, we have a default value like -1 or % and we have a parameter that can be anything. If the parameter is the default value, nothing is filtered.
I suggest % character as the default value if you are querying over a text column or -1 if you are querying over the PK of the table. But it totally depends to you to substitute % or -1 with any reserved character or number that you decide on.
similiar to #brandonmoore:
select * from table_a where table_a.id not in ('0')
How about:
select * from table_a where table_a.id not ine ('somevaluethatwouldneverpossiblyexistintable_a.id')
EDIT:
As much as I would like to continue thinking of a way to solve your problem, I know there isn't a way to so I figure I'll go ahead and be the first person to tell you so I can at least get credit for the answer. It's truly a bittersweet victory though :/
If you provide more info though maybe I or someone else can help you think of another workaround.
Given SQL Server 2008, I have written a simple find in string function as follows:
ALTER FUNCTION [dbo].[FindInString]
(
#FindText VARCHAR(255),
#TextSource VARCHAR(512)
)
RETURNS INT
AS
BEGIN
DECLARE #Result INT
SET #Result = 0
SELECT #Result = CHARINDEX(#FindText, #TextSource)
RETURN #Result
END
The complexity of the find function may change in the future, which is why I wanted to encapsulate it in a function.
Now, when I only have one matching record in a table, this works:
SELECT #FindCount = dbo.FindInString('somestring', (SELECT TableSearch FROM Segments WHERE CID=22793))
However, when the select statement returns more than one, it makes sense as to why an error is thrown.
like to know is what I need to do to still have this work as a simple call, as above?
I only need to know if there is one match (I just need to know if #FindCount > 0), and I'm guessing some sort of a loop may be required, but would like to keep this as simple as possible.
Thanks.
You can use aggregate functions and one select:
select
#FindCount = sum(dbo.FindInString('somestring', TableSearch))
from
Segment
where
CID = 22793
Just take care with this, as FindInString will fire for each row, which can significantly reduce query performance. In this case, it's the only way to solve your problem, but just beware of the troubles that could arise.