T-sql Common expression query as subquery - tsql

I have the following query:
WITH Orders(Id)
AS (
SELECT DISTINCT anfrageid FROM MPHotlineAnfrageAnhang
)
SELECT Id,
(
SELECT CONVERT(VARCHAR(255),anfragetext) + ' | '
FROM MPHotlineAnfrageAnhang
WHERE anfrageid = Id
ORDER BY anfrageid, erstelltam
FOR XML PATH('')
) AS Descriptions
FROM Orders
Its concatenates varchar values of diferents rows grouped by an id. But now i want to include it as a subquery and it gives some errors i cant solve.
Simplified example of use:
select descriptions from
(
WITH Orders(Id)
AS (
SELECT DISTINCT anfrageid FROM MPHotlineAnfrageAnhang
)
SELECT Id,
(
SELECT CONVERT(VARCHAR(255),anfragetext) + ' | '
FROM MPHotlineAnfrageAnhang
WHERE anfrageid = Id
ORDER BY anfrageid, erstelltam
FOR XML PATH('')
) AS Descriptions
FROM Orders
) as tx where id=100012
Errors (Aproximate translation from spanish):
-Incorrect sintaxis near 'WITH'.
-Incorrect sintaxis near 'WITH'. If the instruction is a common table expression or a xmlnamespaces clause, the previous instruction must end with semicolon.
-Incorrect sintaxis near ')'.
What im doing wrong?

Chain your queries as CTEs, like this:
WITH Orders(Id) AS (
SELECT DISTINCT anfrageid
FROM MPHotlineAnfrageAnhang
),
OrderDescs AS (
SELECT Id, (
SELECT CONVERT(VARCHAR(255),anfragetext) + ' | '
FROM MPHotlineAnfrageAnhang
WHERE anfrageid = Id
ORDER BY anfrageid, erstelltam
FOR XML PATH('')
) AS Description
FROM Orders
)
SELECT Description
FROM OrderDescs
WHERE Id = 100012
You can have as many CTEs as you like, each referencing the previous, before the actual query.

Also, you need to have a semi-colon before a WITH statement.
;with Orders(id)
Or terminate the previous statement with the semi-colon instead.

Related

Learning Pivoting in TSQL

I feel that this should be simple, but all the pivots I find seem to be more complicated than what I am looking for, so any help or re-direction would be much appreciated.
I have ‘ID_code’ and ‘product_name’ and I am looking for mismatched product names and have them put next to each other in a row as opposed to in a column like this:
Select distinct ID_Code, product_name
From table
Where ID_Code in
(Select ID_Code from table
Group by ID_Code
Having count(distinct product_name) <> 1)
I would like a table set out as
ID_Code Product_name1 Product_name2 Product_name3
Thanks very much, and have a Happy New Year!
This should remove the duplicates but still returns one result if the product_name has a match.
;with testdata as(
SELECT '1' as ID_Code, 'bike' as product_name
UNION ALL SELECT '1', 'biker'
UNION ALL SELECT '1', 'bike'
UNION ALL SELECT '2', 'motorbike'
UNION ALL SELECT '2', 'motorbike'
UNION ALL SELECT '2', 'motorbike'
UNION ALL SELECT '2', 'motrbike'
UNION ALL SELECT '2', 'motorbiker'
)
--added this section to return distinct products
,cte as(
SELECT * FROM testdata d1
INTERSECT
SELECT * FROM testdata d2
)
SELECT --DISTINCT --Use DISTINCT here if need to return just one line per ID_Code
ID_Code
,product_name = STUFF((SELECT ', ' +
--Added this to track product_names for each ID_Code
t2.product_name + '_' + cast(ROW_NUMBER() OVER (PARTITION BY ID_Code ORDER BY product_name) as varchar(100))
FROM cte t2
WHERE t2.ID_Code = cte.ID_Code
FOR XML PATH('')), 1, 2, '')
FROM cte
Example here: db<>fiddle
More info about INTERSECT should this not be what works in this scenario.
Your expected output appears to be somewhat inflexible, because we may not know exactly how many columns/products would be needed. Instead, I recommend and rolling up the mismatched products into a CSV string for output.
SELECT
ID_Code,
STUFF((SELECT ',' + t2.product_name
FROM yourTable t2
WHERE t1.ID_Code = t2.ID_Code
FOR XML PATH('')), 1, 1, '') products
FROM your_table t1
GROUP BY
ID_Code
HAVING
MIN(product_name) <> MAX(product_name); -- index friendly
Demo

Removing all the Alphabets from a string using a single SQL Query [duplicate]

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.

postgresql WITH statement doesn't find auxiliary statements

I have this query:
WITH words_not AS (
SELECT keywords.id
FROM keywords
WHERE keyword = any(array['writing'])
),actes_not AS (
SELECT actes_keywords.acte_id
FROM actes_keywords
WHERE actes_keywords.keyword_id IN (words_not)
)
SELECT actes.id,
actes.acte_date
FROM actes
WHERE actes.id <> all(actes_not);
This returns the following error:
ERROR: column "words_no" does not exist
LINE 1: ...ctes_keywords WHERE actes_keywords.keyword_id IN (mots_non))...
Each auxiliary statement in the WITH query is good (tested) and I thought I was staying pretty close to the manual: https://www.postgresql.org/docs/current/static/queries-with.html
I don't see why the auxiliary statement in the WITH query is not recognised.
You can't use a table reference in an IN (..) clause. You need a sub-query:
WITH words_not AS (
SELECT keywords.id
FROM keywords
WHERE keyword = any(array['writing'])
), actes_not AS (
SELECT actes_keywords.acte_id
FROM actes_keywords
WHERE actes_keywords.keyword_id IN (select id from words_not) --<< HERE
)
SELECT actes.id,
actes.acte_date
FROM actes
WHERE actes.id <> all(select id from actes_not); --<< HERE

How to put variable in JSONB using PostgreSQL?

I am doing an example of indexing with JSONB in PostgreSQL and want add random uuid to a piece of JSON like below. However I can't get the syntax just right the closest I have got is "{"lookup_id": " || uuid || "}".
But I require
{"lookup_id": "92b3b21a-a87c-1798-5d91-3dbf3043c209"}
My code is:
INSERT INTO test (id, json)
SELECT x.id, '{
"lookup_id": " || uuid || "
}'::jsonb
FROM generate_series(1,100) AS x(id),
uuid_in(md5(now()::text)::cstring) AS uuid;
you can use row_to_json function:
select x.id, row_to_json(r.*)::jsonb
from generate_series(1,100) AS x(id)
cross join (select uuid_in(md5(now()::text)::cstring) as lookup_id) as r;
update
first, you can use uuid so you can create unique uids:
CREATE EXTENSION "uuid-ossp";
with cte as (
select
*, uuid_generate_v4() as uuid
from generate_series(1,5) AS x(id)
)
select distinct uuid from cte
------------------------------------------------
"e980c784-8aae-493f-90fb-1091280fe4f7"
"45a80660-3be8-4538-a039-13d97d6306af"
"5380f285-5d6b-467a-a83a-7fdc5c0ebc4c"
"7a435b36-95d3-49fc-808f-359838a866ed"
"3164a544-a2c9-4cd0-b0c4-199a99986cea"
next, merging this to your existing json. The stupid and easiest way for now could be something like this:
with cte as (
select
'{"a":1}'::json as j, uuid_generate_v4() as uuid
from generate_series(1,5) AS x(id)
)
select
left(j::text, length(j::text) - 1) || ', "uuid":' || to_json(uuid) || '}'
from cte
But you can also write some function to merge jsons together, or you can use hstore extension to merge jsons together:
with cte as (
select
id, '{"a":1, "b":2}'::json as data, uuid_generate_v4() as uuid
from generate_series(1,5) AS x(id)
), cte2 as (
select
id,
(
select hstore(array_agg(r.key), array_agg(r.value))
from (
select *
from json_each_text(c.data) as j
union all
select 'uuid', c.uuid::text
) as r
) as data
from cte as c
)
select
id, hstore_to_json(data)
from cte2
And I'm sure bigger experts on PostgreSQL could advice more elegant way to merge jsons together

SQL Server 2008 - split multi-value column into rows with unique values

In a SQL Server 2008 database, I have a column with multiple values separated by semi-colons. Some values contain colons. Sample data:
key:value;key2:value;blah;foo;bar;A sample value:whee;others
key:value;blah;bar;others
A sample value:whee
I want to get all the unique values from each row in separate rows:
key:value
key2:value
blah
foo
bar
A sample value:whee
others
I've looked at various split functions, but they all seem to deal with hard-coded strings, not strings coming from a column in a table. How can I do this?
Edit: Thomas' answer got it! Here was my final query:
With SampleInputs As
(
select distinct myColumn from [myDatabase].[dbo].myTable where myColumn != ''
)
, XmlCte As
(
Select Cast( '<z>' + Replace( myColumn, ';', '</z><z>' ) + '</z>' As xml ) As XmlValue
From SampleInputs As I
)
Select Distinct Y.z.value('.','nvarchar(max)') As Value
From XmlCte
Cross Apply XmlValue.nodes('//z') Y(z)
I'm guessing the XmlValue.nodes and Y.z.value stuff is magic. O_o
With a split function you use cross apply:
select distinct SS.part
from YourTable
cross apply dbo.SplitString(YourColumn, ';') as SS
Here the SplitString takes two arguments, the string column and the separator and has a column called part where the values are returned.
With SampleInputs As
(
Select 'key:value;key2:value;blah;foo;bar;A sample value:whee;others' As [Data]
Union All Select 'key:value;blah;bar;others'
Union All Select 'A sample value:whee'
)
, XmlCte As
(
Select Cast( '<z>' + Replace( I.[Data], ';', '</z><z>' ) + '</z>' As xml ) As XmlValue
From SampleInputs As I
)
Select Distinct Y.z.value('.','nvarchar(max)') As Value
From XmlCte
Cross Apply XmlValue.nodes('//z') Y(z)
Update
Here's a version of the above that handles entities:
With SampleInputs As
(
Select 'key:value;key2:value;blah;foo;bar;A sample value:whee;others' As [Data]
Union All Select 'key:value;blah;bar;others'
Union All Select 'A sample value:whee'
Union All Select 'A sample value:<Oops>'
)
, XmlGoo As
(
Select Cast(
Replace(
Replace( Cast( Z.XmlValue As nvarchar(max) ), '{{', '<z>' )
, '}}', '</z>')
As Xml ) As Xmlvalue
From (
Select Cast(
(
Select '{{' + Replace( [Data], ';', '}}{{' ) + '}}'
From SampleInputs
For Xml Path(''), type
) As Xml ) As XmlValue
) As Z
)
Select Distinct Z.data.value('.', 'nvarchar(max)')
From XmlGoo
Cross Apply XmlValue.nodes('/z') Z(data)