How to create an empty anonymous table in Postgres? - sql

In Postgres if I want to create an "anonymous table" (i.e. a temporary query based on data not in the database) I can use VALUES, for example:
select * from (values (1, 'Hello world'), (100, 'Another row')) as foo (mycol1, mycol2);
But how can I create an anonymous table with no rows? (This is for a code generator, so the question isn't quite as odd as it sounds!). The following does not work
select * from (values ) as foo (mycol1, mycol2);
because I get
ERROR: syntax error at or near ")"
LINE 1: select * from (values ) as foo (mycol1, mycol2);
^
I know a work around
select * from (values (NULL, NULL)) as foo (mycol1, mycol2) where mycol1 is not NULL;
but is there a better or "more official" way?
(I would also be interested to know if it is possible to create a table with no columns!)

I think you can do something like this:
select null::text as a, null::int as b
limit 0

SELECT *
FROM generate_series(0, -1)

Related

Why CONTAINS method doesn't work with REPLACE

I'm using SQL Server 2014 and I'm trying to execute a query with REPLACE method within a CONTAINS method like this:
SELECT *
FROM A
WHERE CONTAINS(Name, REPLACE('abcd', 'a', 'b'))
But the query returns an error
Incorrect syntax near 'REPLACE'.
How can I do it correctly?
You can write as below:
declare #param nvarchar(100)='abcd';
set #param= REPLACE(#param,'a','b');
SELECT *
FROM PersonAddress
WHERE CONTAINS(FullName, #param)
But first, You need to do this
Try with cte
with cte as
(
select
*
from A
where REPLACE('abcd', 'a', 'b') as col
)
select
*
from cte
where contains(col, name)

Behavior of VALUES clause in PostgreSQL

In Postgres, according to its doc, the following works:
select 1 as column1, 'one' as column2
union all
select 2, 'two'
union all
select 3, 'three'
however its extension:
select * from (select 1 as column1, 'one' as column2
union all
select 2, 'two'
union all
select 3, 'three')
results in error:
Similarly, while this works (assuming Postgres creates internal alias?):
values(1,'a'), (2, 'b')
the following results in error, requiring an alias:
select * from (values(1,'a'), (2, 'b'))
and only starts working when such alias is provided:
select * from (values(1,'a'), (2, 'b')) t(z,y)
Same inconsistency seems to also apply to SELECT clause, i.e.:
select 1, 2
works independently but not as a subselect:
select * from (select 1, 2)
unless provided with an alias:
select * from (select 1, 2) t(a, b)
Would it not be a logical extension of the behavior of clauses dealing with sets (SELECT, VALUES, etc.) to assume some internal alias when used inside another select like Postgres already does when use those clauses independently?
What was the reasoning that lead to such inconsistency in design?
The syntax for using VALUES with select is certainly different than how it used in most flavors of SQL with insert. That being said, the issue here with Postgres actually seems to be that the VALUES clause needs to be wrapped in parentheses as a subquery in order for it to be eligible for use with select (it won't work otherwise). Therefore, we can think of the following as being equivalent to any other subquery:
select *
from
(
values (1,'a'), (2, 'b')
) t;
If the VALUES clause were replaced with a select, we would have to alias the subquery, and the same holds true with VALUES. As to why Postgres chose to do this, you might have to check the documentation or ask a question on their forum.

ORA-00917: missing comma when insert not exist in oracle

I have query like this :
INSERT INTO KONTAK (IDKONTAK, NAMA, NOHP, ALAMAT, GROUPKONTAK_FK) VALUES
(SQ_IDKONTAK.NEXTVAL, 'ANDIKA PRATAMA', '+6285226202202', 'JPR', '' WHERE NOT EXISTS
(SELECT * FROM KONTAK WHERE NAMA = 'AMIN'))
I want insert data to KONTAK table based on the values where not exist in KONTAK NAMA='AMIN'.
When I ran it I got error :
ORA-00917: missing comma
Any suggestion?
If you are trying to do a conditional insert, then you can either use MERGE, or use something like this, assuming you want to insert 'ANDIKA PRATAMA' only if 'AMIN' is not already there (which doesn't make sense to me, but it seems to be your goal):
insert into table1(id1, col1, col2)
select sequence1.nextval, 'VAL1', 'VAL2'
from dual
where not exists (select 1 from table1 where col1 = 'AMIN')
http://docs.oracle.com/cd/B19306_01/server.102/b14200/statements_9014.htm
Use Where condition in insert with select statement;
try;
INSERT INTO KONTAK (IDKONTAK, NAMA, NOHP, ALAMAT, GROUPKONTAK_FK)
select
SQ_IDKONTAK.NEXTVAL, 'ANDIKA PRATAMA', '+6285226202202', 'JPR', ''
from dual
WHERE NOT EXISTS (
SELECT * FROM KONTAK WHERE NAMA = 'AMIN'
)

Select where clause by exact numbers

I can use WHERE CLAUSE IN when my COLUMN1 datatype is CHARACTER:
SELECT * FROM TABLE1
WHERE COLUMN1 IN ('A', 'B', 'C');
But when I tried to use WHERE CLAUSE IN when my COLUMN2 datatype is NUMERIC, I will have Syntax Error. As below statement:
SELECT * FROM TABLE2
WHERE COLUMN2 IN (1,2,3);
I know there is a BETWEEN but it's not fulfill what I need as I'm not selecting in Range but in Exact number series.
Thanks.
The one problem that I can think of is when column1 is not a numeric type. This will return an error when you try to compare to an integer (such as using in).
You can try the following to mimic the error:
select *
from (select cast('1' as varchar(255)) as val) t1
where val in (1, 2);
Be careful to have constants be the correct type.

Using tuples in SQL "IN" clause

I have a table containing the fields group_id and group_type and I want to query the table for all the records having any tuple (group id, group type) from a list of tuples. For example, I want to be able to do something like:
SELECT *
FROM mytable
WHERE (group_id, group_type) IN (("1234-567", 2), ("4321-765", 3), ("1111-222", 5))
A very similar question is already asked at: using tuples in sql in clause , but the solution proposed there presumes the tuple list is to be fetched from another table. This doesn't work in my case is the tuple values are hard coded.
One solution is to use string concatenation:
SELECT *
FROM mytable
WHERE group_id + STR(group_type, 1) IN ("1234-5672", "4321-7653", "1111-2225")
But the problem is that the table is quite big and doing a string concatenation and conversion for each record would be very expensive.
Any suggestion?
Given a very minor tweak (replace double quotes with single and add the VALUES keyword), your proposed syntax is valid Standard SQL-92 syntax i.e.
SELECT *
FROM mytable
WHERE (group_id, group_type) IN (
VALUES ('1234-567', 2),
('4321-765', 3),
('1111-222', 5)
);
Sadly, MSFT have not added it to SQL Server and consider it an 'unplanned' feature.
FWIW PostgreSQL and Sqlite are examples of SQL products that support this syntax.
In SQL Server 2008 you can do like this:
select *
from mytable as T
where exists (select *
from (values ('1234-567', 2),
('4321-765', 3),
('1111-222', 5)) as V(group_id, group_type)
where T.group_id = V.group_id and
T.group_type = V.group_type
)
EDIT: this is a dated answer, although it was the accepted answer in 2011, other answers with more upvotes reflect more recent approaches.
Why not construct the OR statements?
SELECT *
FROM mytable
WHERE (group_id = '1234-567' and group_type = 2)
OR (group_id = '4321-765' and group_type = 3)
OR (group_id = '1111-222' and group_type = 5)
Granted, it doesn't look as nice and neat as your concept example but it will do the job (and if you IN with tuples did exist, it would implement it exactly the same way under the covers most likely.
You can use a common table expression to pretend that these tuples are in another table:
;WITH Tuples as (
select '1234-567' as group_id, 2 as group_type union all
select '4321-765', 3 union all
select '1111-222', 5
)
SELECT * /* TODO - Pick appropriate columns */
from mytable m where exists (
select * from Tuples t
where m.group_id = t.group_id and m.group_type = t.group_type)
Using that solution, this should work:
SELECT *
FROM mytable m
WHERE EXISTS (
SELECT * FROM (
SELECT "1234-567" group_id, 2 group_type UNION ALL
SELECT "4321-765", 3 UNION ALL
SELECT "1111-222", 5) [t]
WHERE m.group_id = t.group_id AND m.group_type = t.group_type)
BTW, you should probably use a CTE to create that inner table.
I haven't seen this yet, but something like this should work
SELECT * FROM AgeGroup ag JOIN
(VALUES
('18-24', 18, 24),
('25-34 ', 25, 39),
('35-44 ', 35, 49),
('45-54 ', 45, 59),
('55-64 ', 55, 69),
('65+ ', 65, 299)
) AS x (agegroup, minage, maxage)
ON ag.age_group = x.agegroup
AND ag.min_age=x.minage
AND ag.max_age=x.maxage
Here is another tuple solution using a join:
SELECT
*
FROM mytable m
JOIN
(
SELECT "1234-567" group_id, 2 group_type
UNION ALL SELECT "4321-765", 3
UNION ALL SELECT "1111-222", 5
) [t]
ON m.group_id = t.group_id
AND m.group_type = t.group_type
I had a similar problem but my tuple collection was dynamic - it was sent over to the SQL Server in a query parameter. I came up with the following solution:
Pass a tuple as an XML:
DECLARE #tuplesXml xml = '<tuples><tuple group-id="1234-567" group-type="2"/><tuple group-id="4321-765" group-type="3"/></tuples>';
Inner join the table that you want to filter with the XML nodes:
SELECT t.* FROM mytable t
INNER JOIN #tuplesXml.nodes('/tuples/tuple') AS tuple(col)
ON tuple.col.value('./#group-id', 'varchar(255)') = t.group_id
AND tuple.col.value('./#group-type', 'integer') = t.group_type
It seems to work fine in my situation which is a bit more complex than the one described in the question.
Keep in mind that it is necessary to use t.* instead of * and the table returned from nodes method needs to be aliased (it's tuple(col) in this case).
select * from table_name where 1=1 and (column_a, column_b) in ((28,1),(25,1))