Related
I want to do this inside a plpgsql function
WITH set1 AS (
select *
from table1
where ... -- reduce table1 to the small working set once for multiple reuse
), query_only_for_select_into AS (
select id
into my_variable_declared_earlier
from set1
where foo = 'bar'
)
select my_variable_declared_earlier as my_bar
, *
from set1
where foo <> 'bar'
but Postgres throws the error
ERROR: SELECT ... INTO is not allowed here
I'm guessing it's because the select ... into is in the CTE. But I can't find anything in the documentation or on the web about it. Maybe I just messed up the select ... into syntax?
SQL has no variables - they are part of a procedural language (e.g. PL/pgSQL), not the query language.
But I don't see the reason why you need one:
WITH set1 AS (
select *
from table1
where ... -- reduce table1 to the small working set once for multiple reuse
), query_only_for_select_into AS (
select id as my_variable_declared_earlier
from set1
where foo = 'bar'
)
select qs.my_variable_declared_earlier as my_bar,
*
from set1
join query_only_for_select_into qs on ...
where foo <> 'bar'
If you are certain that query_only_for_select_into only returns a single row, you can use:
select qs.my_variable_declared_earlier as my_bar,
*
from set1
cross join query_only_for_select_into qs
where foo <> 'bar'
SELECT ... INTO variable is PL/pgSQL syntax, and you can only use it with the top level SELECT (the one that returns results. Think of it like that: PL/pgSQL runs an SQL statement and stores the result somewhere.
But you don't need that: simply include query_only_for_select_into in the FROM clause of the main query, then you can access its columns there.
I have to perfrom a lot of queries where the same value is being reused. I thought of something like:
varName = 'value';
select * from sometable t where
t.field1 = varName
or t.field2 = varName;
How can this be done with PostgreSQL 12?
I tried a lot of stuff I found, but nothing seems to work.
I found a solution meanwhile
with varName as (select 'value'::text)
select * from sometable t where
t.field1 = (select * from varName)
or t.field2 = (select * from varName);
Alternatively you can use VALUES inside of your CTE. This will enable you to have multiple values in the same "variable".
Data Sample:
CREATE TABLE t (c1 int, c2 text);
INSERT INTO t VALUES (42,'foo'),(1,'xpto');
Query
WITH j (var) AS (VALUES ('foo'),('bar'))
SELECT t.c1,j.var FROM t
JOIN j ON j.var = t.c2;
c1 | var
----+-----
42 | foo
I have a select query return and it shows the result like below:
select * from table gives the result like below
I have parameter called Apple If I pass the parameter somewhere in query I should get the result like below
How to get this in postgresql. If anyone knows please share the answer below.
I would do this with a helper function for clarity. And it might be reusable.
create or replace function filter_jsonb_array(arr jsonb, fruit text)
returns jsonb language sql immutable as
$$
select coalesce
(
(select jsonb_agg(j) from jsonb_array_elements(arr) j where j ->> 'fruit' = fruit),
'[]'::jsonb
);
$$;
and then
select "Column_A", "Column_B", filter_jsonb_array("Column_JSONARRAY", 'Apple') from table_;
If you do not want a function then the function body can be placed directly into the select query.
select
"Column_A",
"Column_B",
coalesce
(
(select jsonb_agg(j) from jsonb_array_elements("Column_JSONARRAY") j where j ->> 'fruit' = 'Apple'),
'[]'::jsonb
) "Column_JSONARRAY"
from table_;
Considering your datatype of column Column_JSONARRAY is JSONB, try This:
with cte as (
SELECT column_a, column_b, (column_jsonarray ->> ( index_-1 )::int)::jsonb AS "column_jsonarray"
FROM table_
CROSS JOIN jsonb_array_elements(column_jsonarray)
WITH ORDINALITY arr(array_,index_)
WHERE array_->>'fruit' in ('Apple')
)
select t1.column_a, t1.column_b, jsonb_agg(t2.column_jsonarray)
from table_ t1
left join cte t2 on t1.column_a =t2.column_a and t1.column_b =t2.column_b
group by t1.column_a, t1.column_b
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 can someone help me turn this sql into a stored proc..
select * from voilets
where cfrw = 'F16'
UNION
(select *
from voilets
where cfrw in ('B05','B12','R02','F01','F16','F17','U11','U03','U04','U21'))
ORDER BY DSCA
Where 'F16 is a variable called #default
and
'B05','B12','R02','F01','F16','F17','U11','U03','U04','U21' is an array of #voilets
This is not working for me:
#sCarrierSelect varchar(max)
AS
BEGIN
declare #SQL nvarchar(4000)
set #SQL = '
select * from voilets
where t_cfrw = ' + #default + '
UNION
(select *
from carriers
where t_cfrw in (' + #voilets+'))
ORDER BY T_DSCA
'
print #SQL
exec sp_executesql #SQL
END
IF you SQL Server IS >=2008 then:
USE tempdb;
GO
CREATE TABLE voilets
(cfrw char(3), DSCA int)
go
INSERT INTO voilets VALUES ('R02', 2)
INSERT INTO voilets VALUES ('F16', 5)
INSERT INTO voilets VALUES ('F16', 4)
INSERT INTO voilets VALUES ('X77', 9)
go
CREATE TYPE myType AS TABLE (id CHAR(3));
GO
CREATE PROCEDURE usp_myProc
#default char(3),
#voiletsTVP myType READONLY
AS
select * from voilets
where cfrw = #default
UNION
(select *
from voilets
where cfrw in (SELECT * FROM #voiletsTVP))
ORDER BY DSCA
GO
-------------------------
DECLARE #default char(3)
SET #default='F16'
DECLARE #voiletsTVP AS myType;
INSERT INTO #voiletsTVP SELECT * FROM (VALUES ('B05'),('B12'),('R02'),('F01'),('F16'),('F17'),('U11'),('U03'),('U04'),('U21')) q(x)
EXEC usp_myProc #default,#voiletsTVP
GO
Result-set:
cfrw DSCA
R02 2
F16 4
F16 5
Performing that safely in a sproc is actually quite tricky; there are a few common approaches:
use a udf to split a string on a token - google for "split udf" (there will be many), and join on the results
use a table valued parameter
Personally, I rarely use sprocs these days; I'd use dapper:
List<string> foo = ...
var items = conn.Query<SomeType>(
"select * from [table] where colName in #foo", new { foo }).ToList();
Most LINQ providers and ORMs will have options here too, involving Contains etc.
You can learn about Passing Arrays in SQL Parameters using XML Data Type in SQL Server 2005
See sample:
/* for this xml:
<list>
<item>42</item>
<item>73</item>
<item>2007</item>
</list>
*/
CREATE FUNCTION [lm].[SplitList]
(
#list AS XML
)
RETURNS TABLE
AS
RETURN
(
SELECT tempTable.item.value('.', 'VARCHAR(MAX)') AS Item
FROM #list.nodes('list/item') tempTable(item)
);
Why not use a sql CLR function to split your values, passing those into your procedure. Here is a very good and fast split string implementation: CLR Split String. If you can't use sql clr, then look online for 'sql split string'. Whichever you use you put the result of that work into a temporary table and join that to your main table.