Case statement inside create-table - sql

How do you use the "java if-statement" in SQL => PostgreSQL, while creating a table/Column?
CREATE TABLE Store(
Discount INT
AS CASE
WHEN SOLD_Amount>100000 THEN 2
WHEN SOLD_Amount>500000 THEN 5
WHEN SOLD_Amount>1000000 THEN 10
ELSE 0
END
NOT NULL)
This is probally wrong, please tell us, the community how to do this kind of action.

What you are looking for here is a computed column, which is not directly supported by Postgres. You could implement this in a view, like so:
CREATE VIEW someview AS
SELECT SOLD_Amount,
CASE
WHEN SOLD_Amount>100000 THEN 2
WHEN SOLD_Amount>500000 THEN 5
WHEN SOLD_Amount>1000000 THEN 10
ELSE 0
END As Discount
Or you could use a trigger to populate the column on insert/update.

You can use a special PostgreSQL feature: "generated" columns.
Based on an existing table, say:
CREATE TABLE store (sold_amount int, ...):
You could create this special function:
CREATE FUNCTION store_sold_amount(rec store)
RETURNS int LANGUAGE SQL IMMUTABLE
AS
$func$
SELECT CASE
WHEN rec.sold_amount > 100000 THEN 2
WHEN rec.sold_amount > 500000 THEN 5
WHEN rec.sold_amount > 1000000 THEN 10
ELSE 0 END;
$func$;
Then you can query:
SELECT s.amount, s.store_sold_amount
FROM store s;
More under these related questions:
How can I create a column in postgres from values and selections based on other columns?
Store common query as column?

SELECT A.*,
CASE
WHEN B.Table2 IS NOT NULL
THEN 'Yes'
ELSE 'No'
END AS results_column_name_here
INTO new_table_name
FROM Table1 A
LEFT JOIN Table2 B
ON A.col_to_join_on = B.col_to_join_on

Related

Displaying an alternative result when derrived table is empty

I have this sql code where I try to display an alternative value as a result whenever the table is empty or the the single column of the top row when it is not
select top 1 case when count(*)!=0 then derrivedTable.primarykey
else 0 end endCase
from
(
select top 1 m.primarykey
from mytable m
where 0=1
)derrivedTable
The problem is that when I run this, I get the error message "column 'derrivedTable.primarykey' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause."
But when I put 'derrivedTable.primarykey' in the group by clause, I just get an empty table.
Does anyone hve a solution?
thanks in advance
You can use aggregation:
select coalesce(max(m.primarykey), 0)
from mytable m;
An aggregation query with no group by always returns exactly one row. If the table is empty (or all rows are filtered out), then the aggregation functions -- except for COUNT() -- return NULL -- which can be transformed to a value using COALESCE().
Such a construct makes me worry. If you are using this to set the primary key on an insert, then you should learn about identity columns or sequences. The database will do the work for you.
Can you try this below script-
SELECT
CASE
WHEN COUNT(*) = 1 THEN derrivedTable.primarykey
ELSE 0
END endCase
FROM
(
SELECT TOP 1 m.primarykey
FROM mytable m
WHERE 0 = 1
) derrivedTable
derrivedTable.primarykey;

SQL - Retrieve records based on parameters where either parameter can be null

I have a stored procedure which takes in five parameters of which two can be null - we will call these parameters A and B
What I would like to do is select records based on the following logic.
If Parameter A is NULL then only return records that match Parameter B
I know that I can do something similar to the following
IF A IS NULL
BEGIN
SELECT * FROM TABLE WHERE Param=B
END
ELSE
BEGIN
SELECT * FROM TABLE WHERE Param=A
END
However, the SQL query is much more complex then the above one and there would be huge replication in the Proc which is something I want to avoid
Thanks in advance
===============================
EDIT - Sorry, I should have mentioned that in the example the Param are based on separate columns e.g.
My table consists of four columns of which two separate columns map to the two parameters - basic schema below
ID
PersonName
GroupID
DeliveryID
In my procedure I want to retrieve those records that match the GroupID however in the scenario where the GroupID is null then I want to return those records that match the DeliveryID
Thanks again
Try
SELECT * FROM my_table WHERE Param = COALESCE(A,B)
COALESCE will give you A if it's not null. Otherwise B.
Functionally, something like this should work. If either parameter is NULL, the condition becomes a self-identity (assuming neither groupID nor deliveryID is NULL).
SELECT *
FROM table_name
WHERE groupID = coalesce(#groupIDParameter, groupID)
AND deliveryID = coalesce(#deliveryIDParameter, deliveryID)
Try ISNULL function:
SELECT * FROM TABLE WHERE Param = ISNULL(B,A)
You could also use a case statement Case when A is Null Then B

teradata case when issue

I have the following queries which are supposed to give the same result, but drastically different
1.
select count(*)
from qigq_sess_parse_2
where str_vendor = 'natural search' and str_category is null and destntn_url = 'http://XXXX.com';
create table qigq_test1 as
(
select case
when (str_vendor = 'natural search' and str_category is null and destntn_url = 'http://XXXX.com' ) then 1
else 0
end as m
from qigq_sess_parse_2
) with data;
select count(*) from qigq_test1 where m = 1;
the first block gives a total number of count 132868, while the second one only gives 1.
What are the subtle parts in the query that causes this difference?
Thanks
When you create a table in Teradata, you can specify it to be SET or MULTISET. If you don't specify, it defaults to SET. A set table cannot contain duplicates. So at most, your new table will contain two rows, a 0 and a 1, since that's all that can come from your case statement.
EDIT:
After a bit more digging, the defaults aren't quite that simple. But in any case, I suspect that if you add the MULTISET option to your create statement, you'll see the behavior your expect.
My guess would be that your Create Table statement is only pulling in one row of data that fits the parameters for the following Count statement. Try this instead:
CREATE TABLE qigq_test1 (m integer);
INSERT INTO qigq_test1
SELECT
CASE
WHEN (str_vendor = 'natural search' and str_category IS NULL AND destntn_url = 'http://XXXX.com' ) THEN 1
ELSE 0
END AS m
FROM qigq_sess_parse_2;
SELECT COUNT(*) FROM qigq_test1 WHERE m = 1;
This should pull ALL ROWS of data from qigq_sess_parse_2 into qigq_test1 as either a 0 or 1.

How to return 0 if table empty, 1 otherwise

In postgreSQL, how can i return a table containing 0 if my table is empty and a table containing 1 if my table has rows?
I need to do it in SQL, not using any other language
Use:
SELECT CASE
WHEN EXISTS (SELECT * FROM foo LIMIT 1) THEN 1
ELSE 0
END
EDIT: Added LIMIT 1 to speed up query.
Might be a hack, but it works.
SELECT count(*) FROM (SELECT 1 FROM table LIMIT 1) AS t;
The 1 selected in the sub-query could be whatever, it is just a place holder.
The LIMIT 1 should make the sub-query very fast, regardless of table-size.
Edit: Rewrote a bit. Previous use of LIMIT was wrong (didn't help on large tables as I intended).
Try:
select sign(count(*)) from mytable
maybe this is what you are looking for?
select min( c ) from (
select count(*) c
from mytab
union
select 1
from mytab
having count(*) > 1 )
SELECT, COUNT and LIMIT should do it.
-- see below SELECT COUNT(*) FROM X LIMIT 1
Edit: This doesn't work in Postgres (8.x at least). Please see the comments and here: http://postgresql.1045698.n5.nabble.com/window-function-count-and-limit-td3233588.html
Happy SQL'ing.
You can put this request in a stored procedure, with the table name as parameter :
CREATE OR REPLACE FUNCTION isEmpty(tableName text, OUT zeroIfEmpty integer) AS
$func$
BEGIN
EXECUTE format('SELECT COALESCE ((SELECT 1 FROM %s LIMIT 1),0)', tableName)
INTO zeroIfEmpty;
END
$func$ LANGUAGE plpgsql;
Then run this function like this :
SELECT * FROM isEmpty('my_table_name');
So you can call it with any of your table's name
I don't think that's possible using nothing but SQL. Why can't you use any other language?

Iterate through "linked list" in one SQL query?

I have a table that looks basically like this:
id | redirectid | data
where the redirectid is an id to another row. Basically if a row is selected, and it has a redirectid, then the redirectid data should be used in it's place. There may be multiple redirects until redirectid is NULL. Essentially these redirects form a linked list in the table. What I'd like to know is, given an id, is it possible to set up a sql query that will iterate through all the possible redirects and return the id at the end of the "list"?
This is using Postgresql 8.3 and I'd like to do everything in on sql query if possible (rather than iterate in my code).
Does postgresql support recursive queries that use WITH clauses? If so, something like this might work. (If you want a tested answer, provide some CREATE TABLE and INSERT statements in your question, along with the results you need for the sample data in the INSERTs.)
with Links(id,link,data) as (
select
id, redirectid, data
from T
where redirectid is null
union all
select
id, redirectid, null
from T
where redirectid is not null
union all
select
Links.id,
T.redirectid,
case when T.redirectid is null then T.data else null end
from T
join Links
on Links.link = T.id
)
select id, data
from Links
where data is not null;
Additional remarks:
:( You can implement the recursion yourself based on the WITH expression. I don't know postgresql syntax for sequential programming, so this is a bit pseudo:
Insert the result of this query into a new table called Links:
select
id, redirectid as link, data, 0 as depth
from T
where redirectid is null
union all
select
id, redirectid, null, 0
from T
where redirectid is not null
Also declare an integer ::depth and initialize it to zero. Then repeat the following until it no longer adds rows to Links. Links will then contain your result.
increment ::depth;
insert into Links
select
Links.id,
T.redirectid,
case when T.redirectid is null then T.data else null end,
depth + 1
from T join Links
on Links.link = T.id
where depth = ::depth-1;
end;
I think this will be better than any cursor solution. In fact, I can't really think of how cursors would be useful for this problem at all.
Note that this will not terminate if there are any cycles (redirects that are ultimately circular).
I'd say you should create a user-defined function in this vein:
create function FindLastId (ID as integer) returns integer as $$
declare newid integer;
declare primaryid integer;
declare continue boolean;
begin
set continue = true;
set primaryid = $1;
while (continue)
select into newid redirectid from table where id = :primaryid;
if newid is null then
set continue = false;
else
set primaryid = :newid;
end if;
end loop;
return primaryid;
end;
$$ language pgplsql;
I'm a bit shaky on the Postgres syntax, so you may have some cleanup to do. Anyway, you can then call your function like so:
select id, FindLastId(id) as EndId from table
On a table like so:
id redirectid data
1 3 ab
2 null cd
3 2 ef
4 1 gh
5 null ij
This will return:
id EndId
1 2
2 2
3 2
4 2
5 5
Note that this will be markedly slow, but it should get you the ID's pretty quickly for a small result set on a well indexed table.