Return a value if select returned null - firebird

I need to return a value if select returned null. however I found a solution here by putting a query in a sub-query
SELECT COALESCE((SELECT id FROM tbl WHERE id = 9823474), 4) AS id FROM RDB$DATABASE;
The query above would return Null because the value 9823474 does not exist in the table but I want to return a value in that case (for ex 4) so I found the only solution to use select inside sub query and then COALESCE would work, If I did not do that COALESCE will also return Null.
Is it the only solution ?

No, that is not an only way for example
Select first 1 id from (
Select id FROM tbl WHERE id = 9823474
Union All
Select 4 from rdb$database)
Or you can use anonymous procedure http://firebirdsql.su/doku.php?id=execute_block
EXECUTE BLOCK RETURNS ( id integer )
AS
BEGIN
IF ( EXISTS (SELECT * FROM tbl WHERE id = 9823474) )
THEN id = 9823474;
ELSE id = 4;
SUSPEND;
END
... there always are many methods there

Related

Coalesce sentence containing an insert into clause fails in PostgreSQL

This is my trivial test table,
create table test (
id int not null generated always as identity,
first_name. varchar,
primary key (id),
unique(first_name)
);
As an alternative to insert-into-on-conflict sentences, I was trying to use the coalesce laziness to execute a select whenever possible or an insert, only when select fails to find a row.
coalesce laziness is described in documentation. See https://www.postgresql.org/docs/current/functions-conditional.html
Like a CASE expression, COALESCE only evaluates the arguments that are needed to determine the result; that is, arguments to the right of the first non-null argument are not evaluated. This SQL-standard function provides capabilities similar to NVL and IFNULL, which are used in some other database systems.
I also want to get back the id value of the row, having being inserted or not.
I started with:
select coalesce (
(select id from test where first_name='carlos'),
(insert into test(first_name) values('carlos') returning id)
);
but an error syntax error at or near "into" was found.
See it on this other DBFiddle
https://www.db-fiddle.com/f/t7TVkoLTtWU17iaTAbEhDe/0
Then I tried:
select coalesce (
(select id from test where first_name='carlos'),
(with r as (
insert into test(first_name) values('carlos') returning id
) select id from r
)
);
Here I am getting a WITH clause containing a data-modifying statement must be at the top level error that I don't understand, as insert is the first and only sentence within the with.
I am testing this with DBFiddle and PostgreSQL 13. The source code can be found at
https://www.db-fiddle.com/f/hp8T1iQ8eS4wozDCBhBXDw/5
Different method: chained CTEs:
CREATE TABLE test
( id INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY PRIMARY KEY
, first_name VARCHAR UNIQUE
);
WITH sel AS (
SELECT id FROM test WHERE first_name = 'carlos'
)
, ins AS (
INSERT INTO test(first_name)
SELECT 'carlos'
WHERE NOT EXISTS (SELECT 1 FROM test WHERE first_name = 'carlos')
RETURNING id
)
, omg AS (
SELECT id FROM sel
UNION ALL
SELECT id FROM ins
)
SELECT id
FROM omg
;
It seems that the returning value from the insert into clause is not equivalent in nature to the scalar query of a select clause. So I try encapsulating the insert into into an SQL function and it worked.
create or replace function insert_first_name(
_first_name varchar
) returns int
language sql as $$
insert into test (first_name)
values (_first_name)
returning id;
$$;
select coalesce (
(select id from test where first_name='carlos'),
(select insert_first_name('carlos'))
);
See it on https://www.db-fiddle.com/f/73rVXgqGfrG4VmjrAk6Z3i/2
This is a refinement on #wildplasser accepted answer. it avoids comparing first_name twice and uses coalesce instead of union all. Kind of an selsert in just one sentence.
with sel as (
select id from test where first_name = 'carlos'
)
, ins as (
insert into test(first_name)
select 'carlos'
where (select id from sel) is null
returning id
)
select coalesce (
(select id from sel),
(select id from ins)
);
See it at https://www.db-fiddle.com/f/goRh4TyAebTkEZFHk6WbtK/6

Checking if string is in table

Hi I have table called mytable and 1 column first_name , how can I check if 'John' is in this table and return the result as true/false.
You can utilize a EXISTS-query:
select exists (select * from mytable where mytable.first_name = 'John')
The above query will return a boolean which will be true if the sub-query inside the braces returns any rows at all; the boolean will false if the sub-query return no rows.
SELECT count(*) FROM mytable WHERE first_name LIKE 'John';
returns number of occurrences. If there is no John in table, returns zero.
SELECT CASE WHEN COUNT(*) > 0 THEN 'true' ELSE 'false' END
FROM mytable
WHERE first_name LIKE '%John%'

Most effective way to get value if select count(*) = 1 with grouping

Lets say I have table with ID int, VALUE string:
ID | VALUE
1 abc
2 abc
3 def
4 abc
5 abc
6 abc
If I do select value, count(*) group by value I should get
VALUE | COUNT
abc 5
def 1
Now the tricky part, if there is count == 1 I need to get that ID from first table. Should I be using CTE? creating resultset where I will add ID string == null and run update b.ID = a.ID where count == 1 ?
Or is there another easier way?
EDIT:
I want to have result table like this:
ID VALUE count
null abc 5
3 def 1
If your ID values are unique, you can simply check to see if the max(id) = min(id). If so, then use either one, otherwise you can return null. Like this:
Select Case When Min(id) = Max(id) Then Min(id) Else Null End As Id,
Value, Count(*) As [Count]
From YourTable
Group By Value
Since you are already performing an aggregate, including the MIN and Max function is not likely to take any extra (noticeable) time. I encourage you to give this a try.
The way I would do it would indeed be a CTE:
using #group AS (SELECT value, Count(*) as count from MyTable GROUP BY value HAVING count = 1)
SELECT MyTable.ID, #group.value, #group.count from MyTable
JOIN #group ON #group.value = MyTable.value
When using group by, after the group by statement you can use a having clause.
So
SELECT [ID]
FROM table
GROUP BY [VALUE]
HAVING COUNT(*) = 1
Edit: with regards to your edited question: this uses some fun joins and unions
CREATE TABLE #table
(ID int IDENTITY,
VALUE varchar(3))
INSERT INTO #table (VALUE)
VALUES('abc'),('abc'),('def'),('abc'),('abc'),('abc')
SELECT * FROM (
SELECT Null as ID,VALUE, COUNT(*) as [Count]
FROM #table
GROUP BY VALUE
HAVING COUNT(*) > 1
UNION ALL
SELECT t.ID,t.VALUE,p.Count FROM
#table t
JOIN
(SELECT VALUE, COUNT(*) as [Count]
FROM #table
GROUP BY VALUE
HAVING COUNT(*) = 1) p
ON t.VALUE=p.VALUE
) a
DROP TABLE #table
maybe not the most efficient but something like this works:
SELECT MAX(Id) as ID,Value FROM Table WHERE COUNT(*) = 1 GROUP BY Value

How to filter records for all rows?

I am designing a SQL query to extract all records from a given table. But the trick here is that this logic is based on a numeric database field. So there are 4 choices: 0,1,2,3. If user selects 0,1, or 2, then my query returns rows with the specified value. But if they choose 3, it should return all of the rows. How do I do this in SQL? I know if this was a string, I could do something like:
WHERE = CASE WHEN = 3 THEN '%' ELSE END
But in this case, is an integer. Sounds relatively simple but I'm getting errors.
Try this:
SELECT *
FROM <YOUR_TABLE>
WHERE
(
<YOUR_COLUMN> = #InputValue OR
3 = #InputValue
)
Where #InputValue is the name of parameter sent to the query.
The simplest way is to do this:
select MyColumn
from MyTable
where ( MyValue = #MyParameter or #MyParameter = 3)
If your interested in better optimization, then you can do this, but it is less maintainable:
if (#MyParameter = 3)
select MyColumn
from MyTable
else
select MyColumn
from MyTable
where MyValue = #MyParameter
If I were forced to implement this functionality, then I would probably do this, just to make things clear:
declare #AllRecords nchar(1)
if (#MyParameter = 3)
set #AllRecords = N'Y'
else
set #AllRecords = N'N'
select MyColumn
from MyTable
where (MyValue = #MyParameter or #AllRecords = N'Y')
Hopefully, I won't ever have to implement a system that mixes flags and data value in this way.
UPDATED
Here is a version that should work with your expanded requirements (this requires one of the newer versions of SQL Server, I think):
declare #SelectedLevels table (LevelId int not null primary key)
if #LevelId = 3
insert into #SelectedLevels (LevelId) values (1), (2)
else if #LevelId = 5
insert into #SelectedLevels (LevelId) values (0), (1), (2)
else
insert into #SelectedLevels (LevelId) values (#LevelId)
select mt.MyColumn
from MyTable mt
inner join #SelectedLevels sl on sl.LevelId = MyTable.LevelId
if #Param = 3
begin
select *
from #T
end
else
if #Param = 2
begin
select *
from #T
where id in (0,1)
end
else
begin
select *
from #T
where id = #Param
end

tsql - using internal stored procedure as parameter is where clause

I'm trying to build a stored procedure that makes use of another stored procedure. Taking its result and using it as part of its where clause, from some reason I receive an error:
Invalid object name 'dbo.GetSuitableCategories'.
Here is a copy of the code:
select distinct top 6 * from
(
SELECT TOP 100 *
FROM [dbo].[products] products
where products.categoryId in
(select top 10 categories.categoryid from
[dbo].[GetSuitableCategories]
(
-- #Age
-- ,#Sex
-- ,#Event
1,
1,
1
) categories
ORDER BY NEWID()
)
--and products.Price <=#priceRange
ORDER BY NEWID()
)as d
union
select * from
(
select TOP 1 * FROM [dbo].[products] competingproducts
where competingproducts.categoryId =-2
--and competingproducts.Price <=#priceRange
ORDER BY NEWID()
) as d
and here is [dbo].[GetSuitableCategories] :
if (#gender =0)
begin
select * from categoryTable categories
where categories.gender =3
end
else
begin
select * from categoryTable categories
where categories.gender = #gender
or categories.gender =3
end
I would use an inline table valued user defined function. Or simply code it inline is no re-use is required
CREATE dbo.GetSuitableCategories
(
--parameters
)
RETURNS TABLE
AS
RETURN (
select * from categoryTable categories
where categories.gender IN (3, #gender)
)
Some points though:
I assume categoryTable has no gender = 0
Do you have 3 genders in your categoryTable? :-)
Why do pass in 3 parameters but only use 1? See below please
Does #sex map to #gender?
If you have extra processing on the 3 parameters, then you'll need a multi statement table valued functions but beware these can be slow
You can't use the results of a stored procedure directly in a select statement
You'll either have to output the results into a temp table, or make the sproc into a table valued function to do what you doing.
I think this is valid, but I'm doing this from memory
create table #tmp (blah, blah)
Insert into #tmp
exec dbo.sprocName