SQL Server using a sequence in conjunction with union selects - sql

CREATE SEQUENCE id AS INTEGER START WITH 1 INCREMENT BY 1;
WITH source (Id, SomeColumn) AS
(
SELECT NEXT VALUE FOR id, 'some value 1'
UNION
SELECT NEXT VALUE FOR id, 'some value 2'
)
SELECT * FROM source;
DROP SEQUENCE id;
It throws the following error:
NEXT VALUE FOR function is not allowed in check constraints, default
objects, computed columns, views, user-defined functions, user-defined
aggregates, user-defined table types, sub-queries, common table
expressions, derived tables or return statements.
Why does the NEXT VALUE FOR function not work with unions?
What is a good alternative?
I am using a similar CTE to seed a table with default values using a merge statement. I am trying to avoid manually typing in a sequence of numbers.
I need something like UNION SELECT id++

Based on Alex's comment and link to a similar problem here is the solution:
WITH source (SomeColumn) AS
(
SELECT 'some value 1'
UNION SELECT 'some value 2'
)
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)), SomeColumn FROM source;
For future reference: ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) is the way to get row numbers without ordering a set

Related

Redshift - CASE statement checking column EXISTS or no

I am querying dynamically tables where some of the tables might not have specific column. My intention is check the existence of the column and dynamically assign a value. Basically if all the tables would contain the field I would just write simply :
select name, count(k_val) from tbl GROUP by 1
But in my case I need to do something like this:
select name,
SUM( (CASE when (select EXISTS( SELECT * FROM pg_table_def WHERE tablename = 'tbl'
and "column" = 'k_val'))
then 1 else 0 end) ) as val
from tbl GROUP by 1
I am getting the error:
SQL Error [500310] [0A000]: Amazon Invalid operation:
Specified types or functions (one per INFO message) not supported on
Redshift tables.;
The following is a trick that works on most databases to handle missing columns.
select t.*,
(select k_val -- intentionally not qualified
from tbl t2
where t2.pk = t.pk
) new_k_val
from tbl t cross join
(select NULL as k_val) k;
pk is the primary key column for the table. This uses scoping rules to find a value for k_val. If k_val is in the table, then the subquery will use the value from that row. If not, then the scope will "reach out" and take the value from k. There is no confusion in this case, because k_val is not in tbl.
If you don't want a constant subquery for some reason, you can always use:
(select NULL as k_val from t limit 1) k
You can then use this as a subquery or CTE for your aggregation purposes.
Having said all that, I am wary of handling missing columns this way.

Using an Oracle sequence when the data isn't a complete sequence

Given a DB table containing a unique sequence as primary key, and an additional value VAL;
I need to retrieve the values that match 'like %b', but I need to retrieve them one at a time in a sequence. If the values were consecutive, I would just use an Oracle sequence to work through PKEY using nextval, currval etc.
The problem here is that if the sequence is 4, and I go for nextval, it gives me an incorrect value (a %a value instead of a %b value).
The one solution I can think of is to use the sequence with 'more than' rather than 'equals', so for example (excuse the pseudocode, I can work out correct syntax later);
select pkey, val from table where val like '%b' and pkey>seq.currval
And then shift the sequence past the value of pkey (again using pseudocode for clarity);
alter sequence seq set currval=pkey
For a little context, this table will be accessed by many virtual users as part of a performance test, they each need to be able to run a query that will get them a unique value (so vuser 1 gets 58b, vuser2 gets 24b, etc). The data cannot be ordered or filtered beforehand.
Do you just want to count the values that end in b?
select pkey, val,
(case when val like '%b'
then row_number() over (partition by (case when val like '%b' then 1 else 0 end) order by pkey
)
end) as b_seq
from table;
You can use this as a subquery if you want to find the row corresponding to a particular row of b_seq.
If you don't care about the other values, this is simpler to write with a where clause:
select pkey, val,
row_number() over (order by pkey) as b_seq
from table
where val like '%b';
Building on Gordon's answer I would do:
with
x as (
select seq.nextval as s
),
y as (
select pkey, val,
row_number() over (order by pkey) as b_seq
from table
where val like '%b'
)
select y.* from y join x on y.b_seq = x.s
This way each request will increment the sequence and will return a different SINGLE value from the "b subset".

How the change the cte column value

I want to get the CTE each column value stored in a variable then perform some operation on it. At last stored variable values into other table. But there are more than 10 records in a CTE so I am confused how i do this?
Declare #LineRead nvarchar(max)
;with cte(ID,RecordLine) as (
select Id,RecordLine from [dbo].[WorkDataImport]
)
select #LineRead = RecordLine + 'TEmp'
print LineRead
Result is
xyzaaddda Temp
I dont know why i get only one records.
That's because you are using SELECT for variable assignation.
SQL Server supports nonstandard assignment SELECT statement, which allows querying
data and assign multiple values obtained from the same row to multiple variables by using a single statement.
The assignment SELECT has predictable behavior when exactly one row qualifies. However, if the query has more than one qualifying row, the code doesn’t fail. The assignments take place per each qualifying row, and with each row accessed, the values from the current row overwrite the existing values in the variables. When the assignment SELECT finishes, the values in the variables are those from the last row that SQL Server happened to access.
That's why you are getting only one row.
Replace the SELECT with SET and the code will throw error as:
SET #LineRead = RecordLine + 'TEmp'
One way is to save all the rows from CTE to temp table and then perform the manipulations as:
;with cte(ID,RecordLine) as (
select Id,RecordLine from [dbo].[WorkDataImport]
)
select RecordLine + 'TEmp' as LineRead
into #Temp1
from cte
select * from #Temp1
Demo
Try as below:
;with mycte(ID,RecordLine) as (
select Id,RecordLine from [dbo].[WorkDataImport]
)
select RecordLine + 'TEmp' into #temp from mycte
THEN retrieve all the records from #temp(temp table)
select * from #temp

Create temporary table with fixed values

How do I create a temporary table in PostgreSQL that has one column "AC" and consists of these 4-digit values:
Zoom
Inci
Fend
In essence the table has more values, this should just serve as an example.
If you only need the temp table for one SQL query, then you can hard-code the data into a Common Table Expression as follows :
WITH temp_table AS
(
SELECT 'Zoom' AS AC UNION
SELECT 'Inci' UNION
SELECT 'Fend'
)
SELECT * FROM temp_table
see it work at http://sqlfiddle.com/#!15/f88ac/2
(that CTE syntax also works with MS SQL)
HTH

Sql query that numerates the returned result

How to write one SQL query that selects a column from a table but returns two columns where the additional one contains an index of the row (a new one, starting with 1 to n). It must be without using functions that do that (like row_number()).
Any ideas?
Edit: it must be a one-select query
You can do this on any database:
SELECT (SELECT COUNT (1) FROM field_company fc2
WHERE fc2.field_company_id <= fc.field_company_id) AS row_num,
fc.field_company_name
FROM field_company fc
SET NOCOUNT ON
DECLARE #item_table TABLE
(
row_num INT IDENTITY(1, 1) NOT NULL PRIMARY KEY, --THE IDENTITY STATEMENT IS IMPORTANT!
field_company_name VARCHAR(255)
)
INSERT INTO #item_table
SELECT field_company_name FROM field_company
SELECT * FROM #item_table
if you are using Oracle or a database that supports Sequence objects, make a new db sequence object for this purpose. Next create a view, and run this.
insert into the view as select column_name, sequence.next from table
In mysql you can :
SELECT Row,Column1
FROM (SELECT #row := #row + 1 AS Row, Column1 FROM table1 )
As derived1
I figured out a hackish way to do this that I'm a bit ashamed of. On Postgres 8.1:
SELECT generate_series, (SELECT username FROM users LIMIT 1 OFFSET generate_series) FROM generate_series(0,(SELECT count(*) - 1 FROM users));
I believe this technique will work even if your source table does not have unique ids or identifiers.
On SQL Server 2005 and higher, you can use OVER to accomplish this:
SELECT rank() over (order by company_id) as rownum
, company_name
FROM company