Hi all I have query in oracle as follows
DECLARE in_variable Varchar;
Select Row_Number()
OVER
(
Order By
Decode(in_variable,'column_name ASC',t.column_name) Asc) b
From table t
Converted to sql server as follows
DECLARE #in_variable NVARCHAR(100)
SELECT ROW_NUMBER() OVER
(
ORDER BY
IIF ( #in_sort_by <> '', 'column_name ASC', t.column_name ) ASC )
FROM table t
Is it the correct one or am I doing wrong when I give the value for #in_variable I am getting conversion exception in sql so can some one help me
Rather than using either DECODE or IIF, you'd be better of using CASE. For SQL Server, this would be:
SELECT ROW_NUMBER() OVER
( ORDER BY
CASE WHEN #in_sort_by <> ''
THEN 'column_name ASC'
ELSE t.column_name END ASC )
FROM table t
If you're getting a type conversion error, that would imply that t.column_name is an int. SQL Server will try to convert the static string 'column_name ASC' to match the data type of the column it is being used in place of. To fix this, you can try using CAST to convert the column to VARCHAR:
SELECT ROW_NUMBER() OVER
( ORDER BY
CASE WHEN #in_sort_by <> ''
THEN 'column_name ASC'
ELSE CAST(t.column_name as varchar) END ASC )
FROM table t
However, I think you're probably pursuing the wrong solution here. It looks like you're trying to make the analytic function sort differently based on the variable provided. Providing the alternate column name and sort order as a string is not going to do that. You should probably look questions related to dynamic sorting for how to do this correctly.
Related
Recently started working on Postgres and need to pivot data.
I wrote the following query:
select *
from crosstab (
$$
with tmp_kv as (
select distinct pat_id
,col.name as key, replace(replace(replace(value, '[',''), ']', ''),'"','') as value
from (
select p.Id as pat_id, nullif(kv.key,'undefined')::int as key, trim(kv.value::text,'"') as value
from pat_table p
left join e_table e on e.pat_id = p.id and e.id is null
,jsonb_each_text(p.data) as kv
) t
left join lateral (
select name::text as name from public.config_fields fld
where id = t.key
) col on true
)
select pat_id, key, value
from tmp_kv
where nullif(trim(key),'') is not null
order by pat_id, key
$$,$$
select distinct key from tmp_kv -- (Get error "relation "tmp_kv" does not exist" )
where nullif(trim(key),'') is not null
order by 1
$$
) as (
pat_id bigint
...
...
);
Query works if I take the WITH clause out into temporary table. But will be deploying it to production with read replicas, so need it to be working with a CTE. Is there a way?
The two queries passed as strings to the crosstab() function are separate queries.
A CTE can only be attached to a single query.
What you ask for is strictly impossible.
Since you have to spell out the (static) return type for crosstab() anyway, and the result of the query in the 2nd parameter has to match that, it's pointless to use a query with a dynamic result as 2nd parameter to begin with.
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
How can I ensure that TSQL will not bark at me with
these values returned:
'1.00000000'
or
NULL
or
''
or
'some value'
When i convert to an int
If you are using SQL Server 2012 or later, you may use the TRY_CONVERT function, e.g.
WITH yourTable AS (
SELECT 123 AS intVal UNION ALL
SELECT '123' UNION ALL
SELECT NULL
)
SELECT
intVal,
CASE WHEN TRY_CONVERT(int, intVal) IS NOT NULL THEN 'yes' ELSE 'no' END AS can_parse
FROM yourTable;
Demo
The TRY_CONVERT function will return NULL in this case if it can't convert the input to an integer. So, this is a safe way to probe your data before trying a formal cast or conversion.
Here was the answer I found that worked for me...
TSQL - Cast string to integer or return default value
I'm not on 2012 or higher due to customer...
Don't give me credit though :) I was only good at searching for the answer that worked for me...
Although I changed it from returning null to returning zero since the stupid varchar should be an int column with a default of zero :)
Here's one that works for any value that is truly a VARCHAR and not an int
since VARCHAR is really a variable length string data type
WITH tmpTable AS (
SELECT '123' as intVal UNION ALL
SELECT 'dog' UNION ALL
SELECT '345' UNION ALL
SELECT 'cat' UNION ALL
SELECT '987' UNION ALL
SELECT '4f7g7' UNION ALL
SELECT NULL
)
SELECT
intVal
,case when intVal not like '%[^0-9]%' then 'yes' else 'no' end FROM tmpTable;
credit given to Tim Biegeleisen for his answer above....
All though when characters are found with his solution it will
still error out... hence the changes
Demo
I'm currently doing a data conversion project and need to strip all alphabetical characters from a string. Unfortunately I can't create or use a function as we don't own the source machine making the methods I've found from searching for previous posts unusable.
What would be the best way to do this in a select statement? Speed isn't too much of an issue as this will only be running over 30,000 records or so and is a once off statement.
You can do this in a single statement. You're not really creating a statement with 200+ REPLACEs are you?!
update tbl
set S = U.clean
from tbl
cross apply
(
select Substring(tbl.S,v.number,1)
-- this table will cater for strings up to length 2047
from master..spt_values v
where v.type='P' and v.number between 1 and len(tbl.S)
and Substring(tbl.S,v.number,1) like '[0-9]'
order by v.number
for xml path ('')
) U(clean)
Working SQL Fiddle showing this query with sample data
Replicated below for posterity:
create table tbl (ID int identity, S varchar(500))
insert tbl select 'asdlfj;390312hr9fasd9uhf012 3or h239ur ' + char(13) + 'asdfasf'
insert tbl select '123'
insert tbl select ''
insert tbl select null
insert tbl select '123 a 124'
Results
ID S
1 390312990123239
2 123
3 (null)
4 (null)
5 123124
CTE comes for HELP here.
;WITH CTE AS
(
SELECT
[ProductNumber] AS OrigProductNumber
,CAST([ProductNumber] AS VARCHAR(100)) AS [ProductNumber]
FROM [AdventureWorks].[Production].[Product]
UNION ALL
SELECT OrigProductNumber
,CAST(STUFF([ProductNumber], PATINDEX('%[^0-9]%', [ProductNumber]), 1, '') AS VARCHAR(100) ) AS [ProductNumber]
FROM CTE WHERE PATINDEX('%[^0-9]%', [ProductNumber]) > 0
)
SELECT * FROM CTE
WHERE PATINDEX('%[^0-9]%', [ProductNumber]) = 0
OPTION (MAXRECURSION 0)
output:
OrigProductNumber ProductNumber
WB-H098 098
VE-C304-S 304
VE-C304-M 304
VE-C304-L 304
TT-T092 092
RichardTheKiwi's script in a function for use in selects without cross apply,
also added dot because in my case I use it for double and money values within a varchar field
CREATE FUNCTION dbo.ReplaceNonNumericChars (#string VARCHAR(5000))
RETURNS VARCHAR(1000)
AS
BEGIN
SET #string = REPLACE(#string, ',', '.')
SET #string = (SELECT SUBSTRING(#string, v.number, 1)
FROM master..spt_values v
WHERE v.type = 'P'
AND v.number BETWEEN 1 AND LEN(#string)
AND (SUBSTRING(#string, v.number, 1) LIKE '[0-9]'
OR SUBSTRING(#string, v.number, 1) LIKE '[.]')
ORDER BY v.number
FOR
XML PATH('')
)
RETURN #string
END
GO
Thanks RichardTheKiwi +1
Well if you really can't use a function, I suppose you could do something like this:
SELECT REPLACE(REPLACE(REPLACE(LOWER(col),'a',''),'b',''),'c','')
FROM dbo.table...
Obviously it would be a lot uglier than that, since I only handled the first three letters, but it should give the idea.
I'm working with a client who has a stored procedure with about a dozen parameters.
I need to get the parameter values from tables in the database, then feed these into the stored procedure to get a number value. I then need to join this value to a SELECT statement.
I know that I have to build a temp table in order to join the SP results with my select statement, but this is all new to me and could use some help. Mostly focusing on how to feed field values into the SP. I would also like the Temp table to contain a couple of the parameters as fields so I can join it to my select statement.
any and all help is appreciated.
Thank You
You can capture the parameter values in declared variables. Something like:
DECLARE #Parm1 int, #Parm2 varchar(50) -- Use appropriate names and datatypes
SELECT #Parm1 = Parm1ColumnName, #Parm2=Parm2ColumnName
FROM TableWithParmValues
-- Include a WHERE condition if appropriate
DECLARE #ProcOutput TABLE(outputvalue int) -- use appropriate names and datatypes to match output
INSERT #ProcOuptut
EXECUTE MyProc #ProcParm1 = #Parm1, #ProcParm2 = #Parm2 -- Use appropriate names
Then use the #ProcOutput temp table, and parameter variables as you need with your SELECT.
This is a comment that is better formatted as an answer.
You don't need to create a temporary table, or table variable, to be able to join a numeric result with other data. The following demonstrates various curiosities using SELECTs without explicitly creating any tables:
declare #Footy as VarChar(16) = 'soccer'
select * from (
select 'a' as Thing, 42 as Thingosity
union all
select *
from ( values ( 'b', 2 ), ( 'c', 3 ), ( #Footy, Len( #Footy ) ) ) as Placeholder ( Thing, Thingosity )
) as Ethel cross join
( select 42 as TheAnswer ) as Fred