I have 2 user defined functions which return a table:
Lets say them UDF1 and UDF2
select * from UDF1(param1) -> returns 1 result
select * from UDF2(param2) -> returns 1 result
The problem is when i do
select * from UDF1(param1) union all select * from UDF2(param2) -returns only 1 result.
Ideally it should return 2 results as its a union all.
Can someone help me why this behaviour is observed in sybase?
The exact code is as follows:
Created function as below:
EXEC SQL.
CREATE FUNCTION "ZCHECK_4" (
#COL3_VAL smallint
)
RETURNS TABLE (
"COL1" varchar(000030),
"COL2" varchar(000030),
"COL3" smallint
) AS RETURN SELECT
"ZTESTFUNC"."COL1",
"ZTESTFUNC"."COL2",
"ZTESTFUNC"."COL3"
FROM "ZTESTFUNC" "ZTESTFUNC"
WHERE "ZTESTFUNC"."COL3" = #COL3_VAL
ENDEXEC.
Final Sql view ->Which is returing only 1 row
CREATE VIEW "ZCHECK_5" AS SELECT
"ZCHECK_4"."COL1",
"ZCHECK_4"."COL2",
"ZCHECK_4"."COL3"
FROM "ZCHECK_4"(
CAST(
20 AS TINYINT
)
) "ZCHECK_4"
UNION ALL SELECT
"ZCHECK_4"."COL1",
"ZCHECK_4"."COL2",
"ZCHECK_4"."COL3"
FROM "ZCHECK_4"(
CAST(
10 AS TINYINT
)
) "ZCHECK_4"
Note : the underlying table(ZTESTFUNC) has 2 records which I cross validated.
Apparently for a UDF(User Defined Function) ,syntax after a select statement of a function would be ignored by the Sybase compiler.
Consider the below sceanrio:
Select F1 UNION ALL F2.
F1 and F2 being UDF with parameters, the highlighted text wouldn't be compiled in Sybase.
It might be a limitation of Sybase.
Note :This is not a case with tables or views where Union all works perfectly fine.
Related
Hi i want to create a query which does the following. When the paramter 25 is selected it only runs part A of the query, if any other number is selected run both Table A and B select queries.
Example Below:
DECLARE #Type varchar (200)
select * from
(SELECT sort_code FROM dbo.Test 1
WHERE FUNDING_YEAR = 26)
union
(SELECT sort_code FROM dbo.Test 2
WHERE FUNDING_YEAR = 26)
Where case when #Type = 25 then select * from table2 else table 1
You just need to reference the variable in the WHERE clause
SELECT *
FROM TableA
WHERE #Type = 25
UNION
SELECT *
FROM TableB
The query above will always select everything in TableB and will only select everything in TableA when the variable is equal to 25.
Since you are using SSRS, what I would do is write the query to return all of the rows and then apply a filter in the SSRS report when the Paramater is 25. I wouldn't pass a paramater value to the SQL side unless it greatly reduces the run time of the query.
(I would have put this in a comment.)
Got a tricky one today (Might even just be me):
I have 8 Linked SQL 2012 servers configured to my main SQL server and I need to create table views so that I can filter all these combined table results only using one where clause, currently I use UNION because they all have the same table structures.
Currently my solution looks as follows:
SELECT * FROM [LinkedServer_1].[dbo].[Table] where value = 'xxx'
UNION
SELECT * FROM [LinkedServer_2].[dbo].[Table] where value = 'xxx'
UNION
SELECT * FROM [LinkedServer_3].[dbo].[Table] where value = 'xxx'
UNION
SELECT * FROM [LinkedServer_4].[dbo].[Table] where value = 'xxx'
UNION
SELECT * FROM [LinkedServer_5].[dbo].[Table] where value = 'xxx'
UNION
SELECT * FROM [LinkedServer_6].[dbo].[Table] where value = 'xxx'
UNION
SELECT * FROM [LinkedServer_7].[dbo].[Table] where value = 'xxx'
UNION
SELECT * FROM [LinkedServer_8].[dbo].[Table] where value = 'xxx'
As you can see this is becoming quite ugly because I have a select statement and where clause for each linked server and would like to know if there was a simpler way of doing this!
Appreciate the feedback.
Brakkie101
Instead of using views, you can use inline table-valued functions (a view with parameters). It will not save initial efforts for creating the queries, but could save some work in the future:
CREATE FUNCTION [dbo].[fn_LinkedSever] (#value NVARCHAR(128))
AS
RETURNS TABLE
AS
RETURN
(
SELECT * FROM [LinkedServer_1].[dbo].[Table] where value = #value
UNION
SELECT * FROM [LinkedServer_2].[dbo].[Table] where value = #value
UNION
SELECT * FROM [LinkedServer_3].[dbo].[Table] where value = #value
UNION
SELECT * FROM [LinkedServer_4].[dbo].[Table] where value = #value
UNION
SELECT * FROM [LinkedServer_5].[dbo].[Table] where value = #value
UNION
SELECT * FROM [LinkedServer_6].[dbo].[Table] where value = #value
UNION
SELECT * FROM [LinkedServer_7].[dbo].[Table] where value = #value
UNION
SELECT * FROM [LinkedServer_8].[dbo].[Table] where value = #value
);
Also, if possible, use UNION ALL instead of UNION.
Using the a char-cast in a group-by clause results something unexpected:
select cast(col as char(2)) from (
select 'Abc' as col from dual
union all
select 'Abc' as col from dual
) group by cast(col as char(10));
The result is 'Abc ' (10 characters long).
Intuitively, I would have expected Oracle to return one of the following:
An error: 'not a group-by expression', as the group-by clause is another than the selection clause
A result of length 2 'Ab'.
Replacing cast(col as char(2)) with cast(col as char(3)), Oracle returns an error 'not a group-by expression'. This, again is a very strange behavior.
How can this be explained? What's the reason behind it?
I'm using Oracle SQL 11g.
As was mentioned above, I think there is a misunderstanding going on. o.O
I can't explain why it's doing this, but here's the pattern for the type of query you have:
If you generalize it a bit like this, where [A] and [B] are integers, and [STRING] is whatever text you want:
select cast(col as char([A])) from (
select '[STRING]' as col from dual
union all
select '[STRING]' as col from dual
) group by cast(col as char([B]));
it looks like this always fails if one of the two conditions below is true (there may be others):
( LENGTH([STRING]) < [B] OR LENGTH([STRING] > [B]) and [A] = LENGTH([STRING])
( LENGTH([STRING]) = [B] AND [A] <> LENGTH([STRING]) )
Otherwise, it'll return a row.
But if you take your example that runs and use it in a CREATE TABLE statement, it's going to fail as it sets up the column width to be the 2 and can't fit the 3 character string coming in.
To add to the oddity, if you append something at the start and the end of the string like this:
select '\*'||cast(col as char([A]))||'\*' from (
select '[STRING]' as col from dual
union all
select '[STRING]' as col from dual
) group by cast(col as char([B]));
This will only work if [A] >= [B], otherwise it fails on ORA-01489: result of string concatenation is too long.
Curious...
Is it possible to return a constant table from an IVT? Now i'm using a regular table, and function is just returning data from this table with several additional operations.
So i want to return a table
Value
-----
0,265433
0,0629412
0,671626
I tried something like this:
ALTER FUNCTION [dbo].[GetEigenvector]
(
)
RETURNS TABLE
AS
RETURN
(
SELECT Value = (SELECT 0,265433 UNION ALL
SELECT 0,0629412 UNION ALL
SELECT 0,671626)
)
but it throws an error:
Only one expression can be specified in the select list when the
subquery is not introduced with EXISTS.
You are messing up the syntax for giving an alias to your column:
ALTER FUNCTION [dbo].[GetEigenvector]
(
)
RETURNS TABLE
AS
RETURN
(
SELECT 0.265433 AS Value
UNION ALL
SELECT 0.0629412
UNION ALL
SELECT 0.671626
)
Another syntax:
alter function [dbo].[GetEigenvector]()
returns table as return
select * from
( values (0.265433), (0.0629412), (0.671626) )
as Eigenvector( Value );
I have a coworker looking for this, and I don't recall ever running into anything like that.
Is there a reasonable technique that would let you simulate it?
SELECT PRODUCT(X)
FROM
(
SELECT 3 X FROM DUAL
UNION ALL
SELECT 5 X FROM DUAL
UNION ALL
SELECT 2 X FROM DUAL
)
would yield 30
select exp(sum(ln(col)))
from table;
edit:
if col always > 0
DECLARE #a int
SET #a = 1
-- re-assign #a for each row in the result
-- as what #a was before * the value in the row
SELECT #a = #a * amount
FROM theTable
There's a way to do string concat that is similiar:
DECLARE #b varchar(max)
SET #b = ""
SELECT #b = #b + CustomerName
FROM Customers
Here's another way to do it. This is definitely the longer way to do it but it was part of a fun project.
You've got to reach back to school for this one, lol. They key to remember here is that LOG is the inverse of Exponent.
LOG10(X*Y) = LOG10(X) + LOG10(Y)
or
ln(X*Y) = ln(X) + ln(Y) (ln = natural log, or simply Log base 10)
Example
If X=5 and Y=6
X * Y = 30
ln(5) + ln(6) = 3.4
ln(30) = 3.4
e^3.4 = 30, so does 5 x 6
EXP(3.4) = 30
So above, if 5 and 6 each occupied a row in the table, we take the natural log of each value, sum up the rows, then take the exponent of the sum to get 30.
Below is the code in a SQL statement for SQL Server. Some editing is likely required to make it run on Oracle. Hopefully it's not a big difference but I suspect at least the CASE statement isn't the same on Oracle. You'll notice some extra stuff in there to test if the sign of the row is negative.
CREATE TABLE DUAL (VAL INT NOT NULL)
INSERT DUAL VALUES (3)
INSERT DUAL VALUES (5)
INSERT DUAL VALUES (2)
SELECT
CASE SUM(CASE WHEN SIGN(VAL) = -1 THEN 1 ELSE 0 END) % 2
WHEN 1 THEN -1
ELSE 1
END
* CASE
WHEN SUM(VAL) = 0 THEN 0
WHEN SUM(VAL) IS NOT NULL THEN EXP(SUM(LOG(ABS(CASE WHEN SIGN(VAL) <> 0 THEN VAL END))))
ELSE NULL
END
* CASE MIN(ABS(VAL)) WHEN 0 THEN 0 ELSE 1 END
AS PRODUCT
FROM DUAL
The accepted answer by tuinstoel is correct, of course:
select exp(sum(ln(col)))
from table;
But notice that if col is of type NUMBER, you will find tremendous performance improvement when using BINARY_DOUBLE instead. Ideally, you would have a BINARY_DOUBLE column in your table, but if that's not possible, you can still cast col to BINARY_DOUBLE. I got a 100x improvement in a simple test that I documented here, for this cast:
select exp(sum(ln(cast(col as binary_double))))
from table;
Is there a reasonable technique that would let you simulate it?
One technique could be using LISTAGG to generate product_expression string and XMLTABLE + GETXMLTYPE to evaluate it:
WITH cte AS (
SELECT grp, LISTAGG(l, '*') AS product_expression
FROM t
GROUP BY grp
)
SELECT c.*, s.val AS product_value
FROM cte c
CROSS APPLY(
SELECT *
FROM XMLTABLE('/ROWSET/ROW/*'
PASSING dbms_xmlgen.getXMLType('SELECT ' || c.product_expression || ' FROM dual')
COLUMNS val NUMBER PATH '.')
) s;
db<>fiddle demo
Output:
+------+---------------------+---------------+
| GRP | PRODUCT_EXPRESSION | PRODUCT_VALUE |
+------+---------------------+---------------+
| b | 2*6 | 12 |
| a | 3*5*7 | 105 |
+------+---------------------+---------------+
More roboust version with handling single NULL value in the group:
WITH cte AS (
SELECT grp, LISTAGG(l, '*') AS product_expression
FROM t
GROUP BY grp
)
SELECT c.*, s.val AS product_value
FROM cte c
OUTER APPLY(
SELECT *
FROM XMLTABLE('/ROWSET/ROW/*'
passing dbms_xmlgen.getXMLType('SELECT ' || c.product_expression || ' FROM dual')
COLUMNS val NUMBER PATH '.')
WHERE c.product_expression IS NOT NULL
) s;
db<>fiddle demo
*CROSS/OUTER APPLY(Oracle 12c) is used for convenience and could be replaced with nested subqueries.
This approach could be used for generating different aggregation functions.
There are many different implmentations of "SQL". When you say "does sql have" are you referring to a specific ANSI version of SQL, or a vendor specific implementation. DavidB's answer is one that works in a few different environments I have tested but depending on your environment you could write or find a function exactly like what you are asking for. Say you were using Microsoft SQL Server 2005, then a possible solution would be to write a custom aggregator in .net code named PRODUCT which would allow your original query to work exactly as you have written it.
In c# you might have to do:
SELECT EXP(SUM(LOG([col])))
FROM table;