SQL Select statement - default field value when field doesn't exist - sql

I'm wondering whether there is a way to display some default value for select statement where queries field doesn't exist.
For instance,
SELECT t.name, t.type, t.price, t.brand FROM some_table t;
If the 'brand' field doesn't exist in the some_table I would like this statement to display 'brand' as 'not available'.
Eventually I want to create a view from that select statement.
I'm just curious whether there is a way to do that in PL/SQL.
EDIT:
To avoid confusion, I want the statement to compile and work when the 'brand' column doesn't exist in the table.

You can use COALESCE, change null by not available
SELECT t.name, t.type, t.price, COALESCE(t.brand,'not available') AS brand FROM some_table t;
COALESCE is sql standard, but i dont know if Oracle have it.
EDIT:
I think you have to check the field exist in table first, someting like:
Select count(*) into v_column_exists
from user_tab_cols
where column_name = 'ADD_TMS'
and table_name = 'EMP';
If 1 then EXIST else NOT EXIST, after create the view based on the result.
1:
SELECT t.name, t.type, t.price, t.brand FROM some_table t;
2:
SELECT t.name, t.type, t.price, 'not available' AS brand FROM some_table t;
But i cant see the right way to use this in view.

Unless you go with some really heavy and clunky meta-data gathering where you simply query the master tables to get the data and then unpivot it all into rows, you cannot query a column that doesn't exist because the compiler will start hooking up his stuff and won't find the column.
You can bypass that by using dynamic sql, but then you'll simply have a runtime error instead since you're still querying a column that doesn't exist.
This means that your dynamic SQL will have to exclude the column if it's not in that table, at which point you're better simply removing the column from the static SQL. The only point where dynamic SQL would truly be better is if you have to query like 30+ tables and that you know what you're doing.
So basically ,why do you need to query columns that don't exist? In your case if it's only to be able to preserve an obsolete view, you'd be better to simply maintain your view when it requires updating.

I have just seen the question above. It seems very weird design or requirement. I am posting a code snippet which may suffice your problem but ideally this should not be like this.
--So i get chance to look into the question asked simple way to get a workaround for your problem is to fetch out the columns list from table
var p_lst refcursor;
SET serveroutput ON;
DECLARE
lv_sql LONG;
lv_tab_name VARCHAR2(100);
lv_col_chk VARCHAR2(1000 CHAR);
BEGIN
FOR I IN
(SELECT * FROM ALL_TAB_COLUMNS WHERE OWNER = 'AVROY' AND TABLE_NAME = 'EMP'
)
LOOP
lv_tab_name:=I.TABLE_NAME;
lv_sql :=lv_sql||','||i.column_name;
END LOOP;
lv_sql:='SELECT '||SUBSTR(lv_sql,2,LENGTH(lv_sql));
dbms_output.put_line(lv_sql);
lv_col_chk:=INSTR(UPPER(lv_sql),'BRAND',1);
dbms_output.put_line(lv_col_chk);
IF lv_col_chk = 0 THEN
lv_sql :=SUBSTR(lv_sql,1,LENGTH(lv_sql))||', ''Not_available'' as Brand_col FROM '||lv_tab_name;
dbms_output.put_line(LV_SQL);
ELSE
lv_sql:=SUBSTR(lv_sql,1,LENGTH(lv_sql))||' FROM '||lv_tab_name;
dbms_output.put_line(LV_SQL);
END IF;
OPEN :p_lst FOR lv_sql;
END;
PRINT p_lst;

SELECT t.name, t.type, t.price, NVL(t.brand,"Not available") FROM some_table t;

Related

'In' clause in SQL server with multiple columns

I have a component that retrieves data from database based on the keys provided.
However I want my java application to get all the data for all keys in a single database hit to fasten up things.
I can use 'in' clause when I have only one key.
While working on more than one key I can use below query in oracle
SELECT * FROM <table_name>
where (value_type,CODE1) IN (('I','COMM'),('I','CORE'));
which is similar to writing
SELECT * FROM <table_name>
where value_type = 1 and CODE1 = 'COMM'
and
SELECT * FROM <table_name>
where value_type = 1 and CODE1 = 'CORE'
together
However, this concept of using 'in' clause as above is giving below error in 'SQL server'
ERROR:An expression of non-boolean type specified in a context where a condition is expected, near ','.
Please let know if their is any way to achieve the same in SQL server.
This syntax doesn't exist in SQL Server. Use a combination of And and Or.
SELECT *
FROM <table_name>
WHERE
(value_type = 1 and CODE1 = 'COMM')
OR (value_type = 1 and CODE1 = 'CORE')
(In this case, you could make it shorter, because value_type is compared to the same value in both combinations. I just wanted to show the pattern that works like IN in oracle with multiple fields.)
When using IN with a subquery, you need to rephrase it like this:
Oracle:
SELECT *
FROM foo
WHERE
(value_type, CODE1) IN (
SELECT type, code
FROM bar
WHERE <some conditions>)
SQL Server:
SELECT *
FROM foo
WHERE
EXISTS (
SELECT *
FROM bar
WHERE <some conditions>
AND foo.type_code = bar.type
AND foo.CODE1 = bar.code)
There are other ways to do it, depending on the case, like inner joins and the like.
If you have under 1000 tuples you want to check against and you're using SQL Server 2008+, you can use a table values constructor, and perform a join against it. You can only specify up to 1000 rows in a table values constructor, hence the 1000 tuple limitation. Here's how it would look in your situation:
SELECT <table_name>.* FROM <table_name>
JOIN ( VALUES
('I', 'COMM'),
('I', 'CORE')
) AS MyTable(a, b) ON a = value_type AND b = CODE1;
This is only a good idea if your list of values is going to be unique, otherwise you'll get duplicate values. I'm not sure how the performance of this compares to using many ANDs and ORs, but the SQL query is at least much cleaner to look at, in my opinion.
You can also write this to use EXIST instead of JOIN. That may have different performance characteristics and it will avoid the problem of producing duplicate results if your values aren't unique. It may be worth trying both EXIST and JOIN on your use case to see what's a better fit. Here's how EXIST would look,
SELECT * FROM <table_name>
WHERE EXISTS (
SELECT 1
FROM (
VALUES
('I', 'COMM'),
('I', 'CORE')
) AS MyTable(a, b)
WHERE a = value_type AND b = CODE1
);
In conclusion, I think the best choice is to create a temporary table and query against that. But sometimes that's not possible, e.g. your user lacks the permission to create temporary tables, and then using a table values constructor may be your best choice. Use EXIST or JOIN, depending on which gives you better performance on your database.
Normally you can not do it, but can use the following technique.
SELECT * FROM <table_name>
where (value_type+'/'+CODE1) IN (('I'+'/'+'COMM'),('I'+'/'+'CORE'));
A better solution is to avoid hardcoding your values and put then in a temporary or persistent table:
CREATE TABLE #t (ValueType VARCHAR(16), Code VARCHAR(16))
INSERT INTO #t VALUES ('I','COMM'),('I','CORE')
SELECT DT. *
FROM <table_name> DT
JOIN #t T ON T.ValueType = DT.ValueType AND T.Code = DT.Code
Thus, you avoid storing data in your code (persistent table version) and allow to easily modify the filters (without changing the code).
I think you can try this, combine and and or at the same time.
SELECT
*
FROM
<table_name>
WHERE
value_type = 1
AND (CODE1 = 'COMM' OR CODE1 = 'CORE')
What you can do is 'join' the columns as a string, and pass your values also combined as strings.
where (cast(column1 as text) ||','|| cast(column2 as text)) in (?1)
The other way is to do multiple ands and ors.
I had a similar problem in MS SQL, but a little different. Maybe it will help somebody in futere, in my case i found this solution (not full code, just example):
SELECT Table1.Campaign
,Table1.Coupon
FROM [CRM].[dbo].[Coupons] AS Table1
INNER JOIN [CRM].[dbo].[Coupons] AS Table2 ON Table1.Campaign = Table2.Campaign AND Table1.Coupon = Table2.Coupon
WHERE Table1.Coupon IN ('0000000001', '0000000002') AND Table2.Campaign IN ('XXX000000001', 'XYX000000001')
Of cource on Coupon and Campaign in table i have index for fast search.
Compute it in MS Sql
SELECT * FROM <table_name>
where value_type + '|' + CODE1 IN ('I|COMM', 'I|CORE');

one query for many similar tables

I have an Oracle database with many tables that have identical structure (columns are all the same). The table names are similar also. The names of the tables are like table_1, table_2, table_3...
I know this isn't the most efficient design, but I don't have the option of changing this at this time.
In this case, is it possible to make a single sql query, to extract all rows with the same condition across multiple tables (hundreds of tables) without explicitly using the exact table name?
I realize I could use something like
select * from table_1 UNION select * from table_2 UNION select * from table_3...select * from table_1000
But is there a more elegant sql statement that can be run that extracts from all matching table names into one result without having to name each table explicitly.
Something like
select * from table_%
Is something like that possible? If not, what is the most efficient way to write this query?
You can use dbms_xmlgen to query tables using a pattern, which generates an XML document as a CLOB:
select dbms_xmlgen.getxml('select * from ' || table_name
|| ' where some_col like ''%Test%''') as xml_clob
from user_tables
where table_name like 'TABLE_%';
You said you wanted a condition, so I've included a dummy one, where some_col like '%Test%'.
You can then use XMLTable to extract the values back as relational data, converting the CLOB to XMLType on the way:
select x.*
from (
select xmltype(dbms_xmlgen.getxml('select * from ' || table_name
|| ' where some_col like ''%Test%''')) as xml
from user_tables
where table_name like 'TABLE_%'
) t
cross join xmltable('/ROWSET/ROW'
passing t.xml
columns id number path 'ID',
some_col varchar2(10) path 'SOME_COL'
) x;
SQL Fiddle demo which retrieves one matching row from each of two similar tables. Of course, this assumes your table names follow a useful pattern like table_%, but you suggest they do.
This is the only way I know to do something like this without resorting to PL/SQL (and having searched back a bit, was probably inspired by this answer to count multiple tables). Whether it's efficient (enough) is something you'd need to test with your data.
This is kind of messy and best performed in a middle-tier, but I suppose you could basically loop over the tables and use EXECUTE IMMEDIATE to do it.
Something like:
for t in (select table_name from all_tables where table_name like 'table_%') loop
execute immediate 'select blah from ' || t.table_name;
end loop;
You can write "select * from table_1 and table_2 and tabl_3;"

Postgres return a default value when a column doesn't exist

I have a query where I essentially want a fallback value if a certain column is missing. I was wondering if I can handle this purely in my query (rather than probing first and sending a seperate query. In essence i'm looking for an equivalent to COALESCE that handles the case of a missing column.
Imagine the following 2 tables.
T1
id | title | extra
1 A | value
- and -
T2
id | title
1 A
I'd like to be able to SELECT from either of these tables WITH THE SAME QUERY.
eg, if t2 actually had an 'extra' column I could use
SELECT id,title, COALESCE(extra, 'default') as extra
But that only works if the column value is NULL, not when the column is missing entirely.
I would prefer an SQL version but I can accept a PLPGSQL function (with a behaviour similiar to COALLESCE) too.
NOTE to SQL purists: I don't really feel like debating why I want to do this in SQL and not in application logic (or why I won't just add the column permanently to the schema) so please restrict your comments/answers to the specific request and not your opinion on database 'correctness' or whatever else might offend you about this question.
Why does Rowan's hack work (mostly)?
SELECT id, title
, CASE WHEN extra_exists THEN extra::text ELSE 'default' END AS extra
FROM tbl
CROSS JOIN (
SELECT EXISTS (
SELECT FROM information_schema.columns
WHERE table_name = 'tbl'
AND column_name = 'extra')
) AS extra(extra_exists)
Normally, it would not work at all. Postgres parses the SQL statement and throws an exception if any of the involved columns does not exist.
The trick is to introduce a table name (or alias) with the same name as the column name in question. extra in this case. Every table name can be referenced as a whole, which results in the whole row being returned as type record. And since every type can be cast to text, we can cast this whole record to text. This way, Postgres accepts the query as valid.
Since column names take precedence over table names, extra::text is interpreted to be the column tbl.extra if the column exists. Otherwise, it would default to returning the whole row of the table extra - which never happens.
Try to pick a different table alias for extra to see for yourself.
This is an undocumented hack and might break if Postgres decides to change the way SQL strings are parsed and planned in future versions - even though unlikely.
Unambiguous
If you decide to use this, at least make it unambiguous.
A table name alone is not unique. A table named "tbl" can exist any number of times in multiple schemas of the same database, which could lead to very confusing and completely false results. You need to supply the schema name additionally:
SELECT id, title
, CASE WHEN col_exists THEN extra::text ELSE 'default' END AS extra
FROM tbl
CROSS JOIN (
SELECT EXISTS (
SELECT FROM information_schema.columns
WHERE table_schema = 'public'
AND table_name = 'tbl'
AND column_name = 'extra'
) AS col_exists
) extra;
Faster
Since this query is hardly portable to other RDBMS, I suggest to use the catalog table pg_attribute instead of the information schema view information_schema.columns. About 10 times faster.
SELECT id, title
, CASE WHEN col_exists THEN extra::text ELSE 'default' END AS extra
FROM tbl
CROSS JOIN (
SELECT EXISTS (
SELECT FROM pg_catalog.pg_attribute
WHERE attrelid = 'myschema.tbl'::regclass -- schema-qualified!
AND attname = 'extra'
AND NOT attisdropped -- no dropped (dead) columns
AND attnum > 0 -- no system columns
)
) extra(col_exists);
Also using the more convenient and secure cast to regclass. See:
What does regclass mean in Postgresql
You can attach the needed alias to fool Postgres to any table, including the primary table itself. You don't need to join to another relation at all, which should be fastest:
SELECT id, title
, CASE WHEN EXISTS (SELECT FROM pg_catalog.pg_attribute
WHERE attrelid = 'tbl'::regclass
AND attname = 'extra'
AND NOT attisdropped
AND attnum > 0)
THEN extra::text
ELSE 'default' END AS extra
FROM tbl AS extra;
Convenience
You could encapsulate the test for existence in a simple SQL function (once), arriving (almost) at the function you have been asking for:
CREATE OR REPLACE FUNCTION col_exists(_tbl regclass, _col text)
RETURNS bool
LANGUAGE sql STABLE AS
$func$
SELECT EXISTS (
SELECT FROM pg_catalog.pg_attribute
WHERE attrelid = $1
AND attname = $2
AND NOT attisdropped
AND attnum > 0
)
$func$;
COMMENT ON FUNCTION col_exists(regclass, text) IS
'Test for existence of a column. Returns TRUE / FALSE.
$1 .. exact table name (case sensitive!), optionally schema-qualified
$2 .. exact column name (case sensitive!)';
Simplifies the query to:
SELECT id, title
, CASE WHEN col_exists THEN extra::text ELSE 'default' END AS extra
FROM tbl
CROSS JOIN col_exists('tbl', 'extra') AS extra(col_exists);
Using the form with additional relation here, since it turned out to be faster with the function.
Still, you only get the text representation of the column with any of these queries. It's not as simple to get the actual type.
Benchmark
I ran a quick benchmark with 100k rows on pg 9.1 and 9.2 to find these to be fastest:
Fastest:
SELECT id, title
, CASE WHEN EXISTS (SELECT FROM pg_catalog.pg_attribute
WHERE attrelid = 'tbl'::regclass
AND attname = 'extra'
AND NOT attisdropped
AND attnum > 0)
THEN extra::text
ELSE 'default' END AS extra
FROM tbl AS extra;
2nd fastest:
SELECT id, title
, CASE WHEN col_exists THEN extra::text ELSE 'default' END AS extra
FROM tbl
CROSS JOIN col_exists('tbl', 'extra') AS extra(col_exists);
db<>fiddle here
Old sqlfiddle
One way is to look up the information schema table and do a little magic with it.
Something like:
SELECT id, title, CASE WHEN extra_exists THEN extra ELSE 'default' END AS extra
FROM mytable
CROSS JOIN (
SELECT EXISTS (SELECT 1
FROM information_schema.columns
WHERE table_name='mytable' AND column_name='extra') AS extra_exists) extra
Edit: Where 'mytable' needs to be passed in for the table you want to query.

sql insert using select, case and subquery inside

select * into #transacTbl from tmpTrans
insert
select
(case when tmpT.TranStatus = 10
then(
select ID, 'Returned')
else(
select ID, 'GoodSale')
end)
from
(
select * from MainTransacSource
) as tmpT
I want to be able to insert the details of a transaction into a different table with a label if it is a returned or good sale/transaction. I did this to avoid the cursor so please avoid giving a solution using a cursor.
I know the code looks good but what I'm experiencing is that, the case statement only returns one value via subquery.
This is a simplified version of the code; I have at least 6 types of cases and should be able to insert by ROW. I hate to think that I have to repeat each case per column because the actual number of columns is about 38.
You may suggest another work-around if this doesn't fit the logic. Of course, without a cursor.
Without access to your tables and not knowing more about what precisely you want to acheive, try something like this:
select * into #transacTbl from tmpTrans
insert
select tmpT.ID,
(case when tmpT.TranStatus = 10
then 'Returned'
else 'GoodSale'
end)
from
(select * from MainTransacSource) as tmpT <OR simply MainTransacSource tmpT (maybe)>
Cheers.

Check number of records in a database table other than count(*)

I want to check if there are any records in a table for a certain entry. I used COUNT(*) to check the number of records and got it to work. However, when the number of records for an entry is very high, my page loads slowly.
I guess COUNT(*) is causing the problem, but how do I check if the records exist without using it? I only want to check whether any records exist for the entry and then execute some code. Please help me find an alternative solution for this.
Thanks for any help.
There are several ways that may work. You can use exists, which lets the database optimise the method to get the answer:
if exists(select * from ...)
You can use top 1 so that the database can stop after finding the first match:
if (select count(*) from (select top 1 * from ...)) > 0
use select top 1 and check is there is an row
You can try selecting the first entry for given condition.
SELECT id FROM table WHERE <condition> LIMIT 1
I'm not sure if this will be quicker but you can try.
Other possible solution. How do you use count? COUNT(*)? If yes, then try using COUNT(id). As I remember this should be faster.
I would recommend testing to see if at least 1 record exists in the table, that meets your criteria then continue accordingly. For example:
IF EXISTS
(
SELECT TOP 1 Table_Name --Or Your ColumnName
FROM INFORMATION_SCHEMA.Tables -- Or your TableName
)
BEGIN
PRINT 'At least one record exists in table'
END
I found this on codeproject. It's quite handy.
-- Author,,Md. Marufuzzaman
SELECT SYS_OBJ.NAME AS "TABLE NAME"
, SYS_INDX.ROWCNT AS "ROW COUNT"
FROM SYSOBJECTS SYS_OBJ, SYSINDEXES SYS_INDX
WHERE SYS_INDX.ID = SYS_OBJ.ID
AND INDID IN(0,1) --This specifies 'user' databases only
AND XTYPE = 'U' --This omits the diagrams table of the database
--You may find other system tables will need to be ommitted,
AND SYS_OBJ.NAME <> 'SYSDIAGRAMS'
ORDER BY SYS_INDX.rowcnt DESC --I found it more useful to display
--The following line adds up all the rowcount results and places
--the final result into a separate column [below the first resulting table]
COMPUTE SUM(SYS_INDX.ROWCNT)
GO
you should use
select count(1) from
If you are saying (*) it will expand all the column's and then count