Querying into a memory table - sql-server

SQL Server lets you create in-memory tables. But how do you do insert into operation on that?
So for example, I used this code to create my type:
CREATE TYPE dbo.typeTableDelimetedFileSpec
AS TABLE
(
TemplateId INT NOT NULL,
FieldName VARCHAR(50) NOT NUL,
FieldPosition SMALLINT NOT NULL INDEX fpos
)
WITH (MEMORY_OPTIMIZED = ON);
Then, I tried to do this:
DECLARE #T [dbo].[typeTableDelimetedFileSpec]
SELECT *
INTO #T
FROM [dbo].[_DelimetedFileSpec]
WHERE TemplateId = 1
I know the structures match (_DelimetedFileSpec does not have index fpos, but other than that there are no differences).
I get:
Incorrect syntax near '#T'.
Also, just to check out that there are no other errors, I confirmed that the following works fine:
SELECT *
INTO #x
FROM [dbo].[_DelimetedFileSpec]
WHERE TemplateId = 1
Is it possible to somehow insert directly into the memory-table, like this?

I found a way to do it efficiently!
Declare #DeliSpecs [dbo].[typeTableDelimetedFileSpec]
Insert into #DeliSpecs (TemplateId, FieldName, FieldPosition) Select TemplateId, FieldName, FieldPosition from _DelimetedFileSpec where TemplateId = #Id

Related

Searching for multiple patterns in a string in T-SQL

In t-sql my dilemma is that I have to parse a potentially long string (up to 500 characters) for any of over 230 possible values and remove them from the string for reporting purposes. These values are a column in another table and they're all upper case and 4 characters long with the exception of two that are 5 characters long.
Examples of these values are:
USFRI
PROME
AZCH
TXJS
NYDS
XVIV. . . . .
Example of string before:
"Offered to XVIV and USFRI as back ups. No response as of yet."
Example of string after:
"Offered to and as back ups. No response as of yet."
Pretty sure it will have to be a UDF but I'm unable to come up with anything other than stripping ALL the upper case characters out of the string with PATINDEX which is not the objective.
This is unavoidably cludgy but one way is to split your string into rows, once you have a set of words the rest is easy; Simply re-aggregate while ignoring the matching values*:
with t as (
select 'Offered to XVIV and USFRI as back ups. No response as of yet.' s
union select 'Another row AZCH and TXJS words.'
), v as (
select * from (values('USFRI'),('PROME'),('AZCH'),('TXJS'),('NYDS'),('XVIV'))v(v)
)
select t.s OriginalString, s.Removed
from t
cross apply (
select String_Agg(j.[value], ' ') within group(order by Convert(tinyint,j.[key])) Removed
from OpenJson(Concat('["',replace(s, ' ', '","'),'"]')) j
where not exists (select * from v where v.v = j.[value])
)s;
* Requires a fully-supported version of SQL Server.
build a function to do the cleaning of one sentence, then call that function from your query, something like this SELECT Col1, dbo.fn_ReplaceValue(Col1) AS cleanValue, * FROM MySentencesTable. Your fn_ReplaceValue will be something like the code below, you could also create the table variable outside the function and pass it as parameter to speed up the process, but this way is all self contained.
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE FUNCTION fn_ReplaceValue(#sentence VARCHAR(500))
RETURNS VARCHAR(500)
AS
BEGIN
DECLARE #ResultVar VARCHAR(500)
DECLARE #allValues TABLE (rowID int, sValues VARCHAR(15))
DECLARE #id INT = 0
DECLARE #ReplaceVal VARCHAR(10)
DECLARE #numberOfValues INT = (SELECT COUNT(*) FROM MyValuesTable)
--Populate table variable with all values
INSERT #allValues
SELECT ROW_NUMBER() OVER(ORDER BY MyValuesCol) AS rowID, MyValuesCol
FROM MyValuesTable
SET #ResultVar = #sentence
WHILE (#id <= #numberOfValues)
BEGIN
SET #id = #id + 1
SET #ReplaceVal = (SELECT sValue FROM #allValues WHERE rowID = #id)
SET #ResultVar = REPLACE(#ResultVar, #ReplaceVal, SPACE(0))
END
RETURN #ResultVar
END
GO
I suggest creating a table (either temporary or permanent), and loading these 230 string values into this table. Then use it in the following delete:
DELETE
FROM yourTable
WHERE col IN (SELECT col FROM tempTable);
If you just want to view your data sans these values, then use:
SELECT *
FROM yourTable
WHERE col NOT IN (SELECT col FROM tempTable);

Perl, SQL Server use of ##IDENTITY

I have a SQL Server table with an identity column, set to autoincrement.
Coded in Perl, the insert in the code below works fine, in the while loop the fetchrow_array() call returns no data in the #row array.
How do I best retrieve the identity value for use in subsequent SQL statements?
my $term_sql = "INSERT INTO reminder_term(site, name, description, localization) OUTPUT \#\#IDENTITY VALUES(?,?,?,?)";
my $t_stmt = $dbh->prepare($term_sql);
...
$t_stmt->execute($site, $name, $description, $localizer);
while (#row = $t_stmt->fetchrow_array()) {
$referential_key = $row[0];
}
Avoid using the ##IDENTITY value since it's unreliable in the presence of triggers.
Given the example table schema...
create table [dbo].[reminder_term] (
[id] int not null identity(1,1),
[site] nvarchar(10),
[name] nvarchar(10),
[description] nvarchar(10),
[localization] nvarchar(10)
);
You can rework your OUTPUT clause slightly you can capture the new id value by way of the special inserted row source...
INSERT INTO reminder_term(site, name, description, localization)
OUTPUT inserted.id
VALUES(?,?,?,?)

Is it possible to perform a regex pattern match in a SQL Server varchar(max) column and return the match?

I have a log table that has some records that have this type of pattern:
.... "RefundId":"re_1ABasdf234234343434", "..."....
I want to extract and return the value of the RefundId in a column in a select statement, is this possible?
If there is only one Refund_ID for each row then you can use something like this:
--Create table
create table T1 (
T1_id int identity(1,1) primary key clustered,
Log_Data varchar(max) null
)
--Insert test data
insert T1(Log_Data)
values('.... "RefundId":"re_1ABasdf234234343434", "..."....'),
(' "RefundId":"JHHJJHJHJHJJHJH", "..."....'),
(''),
(null)
--Get some results
select *, left(substring(Log_Data, patindex('%"RefundId":"%', Log_Data)+12, 20000000), patindex('%"%', substring(Log_Data, patindex('%"RefundId":"%', Log_Data)+12, 20000000)) + case when patindex('%"%', substring(Log_Data, patindex('%"RefundId":"%', Log_Data)+12, 20000000)) > 0 then -1 else 0 end ) Refund_ID
from T1
If there are multiple Refund_IDs for each value then you will have to find a different method.
You can use the keyword LIKE
SELECT RefundId
FROM MyTable
WHERE RefundId LIKE 'some pattern'

Setting the value of a column in SQL Server based on the scope_identity() of the insert

I have a stored procedure in a program that is not performing well. Its truncated version follows. The MyQuotes table has an IDENTITY column called QuoteId.
CREATE PROCEDURE InsertQuote
(#BinderNumber VARCHAR(50) = NULL,
#OtherValue VARCHAR(50))
AS
INSERT INTO MyQuotes (BinderNumber, OtherValue)
VALUES (#BinderNumber, #OtherValue);
DECLARE #QuoteId INT
SELECT #QuoteId = CONVERT(INT, SCOPE_IDENTITY());
IF #BinderNumber IS NULL
UPDATE MyQuotes
SET BinderNumber = 'ABC' + CONVERT(VARCHAR(10),#QuoteId)
WHERE QuoteId = #QuoteId;
SELECT #QuoteId AS QuoteId;
I feel like the section where we derive the binder number from the scope_identity() can be done much, much, cleaner. And I kind of think we should have been doing this in the C# code rather than the SQL, but since that die is cast, I wanted to fish for more learned opinions than my own on how you would change this query to populate that value.
The following update avoids needing the id:
UPDATE MyQuotes SET
BinderNumber = 'ABC' + CONVERT(VARCHAR(10), QuoteId)
WHERE BinderNumber is null;
If selecting QuoteId as a return query is required then using scope_identity() is as good a way as any.
Dale's answer is better, however this can be useful way too:
DECLARE #Output TABLE (ID INT);
INSERT INTO MyQuotes (BinderNumber, OtherValue) VALUES (#BinderNumber, #OtherValue) OUTPUT inserted.ID INTO #Output (ID);
UPDATE q SET q.BinderNumber = 'ABC' + CONVERT(VARCHAR(10),o.ID)
FROM MyQuotes q
INNER JOIN #Output o ON o.ID = q.ID
;
Also, if BinderNumber is always linked to ID, it would be better to just create computed column
AS 'ABC' + CONVERT(VARCHAR(10),ID)

msforeachdb with multiple queries into a single result set creating a temp table

I am trying to run a report to get some metrics about our users and the different applications we have. Each of our customers have a different database so I need to run the same queries across several databases. The query I use works like a charm but then I need to manually copy and paste each of the results to make everything readable. So I thought I would create a temp table, then insert each query result into a different column in the table to also avoid duplicate code but somehow, most of the results returned are null or show numbers that do not much when running the query without using the temp table. Any ideas as to what I am might be doing wrong? Can't seem to figure it out
DROP TABLE #ReportAlexis
CREATE TABLE #ReportAlexis
(
CompanyName VARCHAR(MAX),
TotalUsers INT,
UsersSinceDate INT,
TotalAppUsers INT,
AppUsersSinceDate INT,
Number_of_Logins_SinceDate INT,
);
EXEC master.dbo.sp_msforeachdb 'if ''?'' in (''master'',''model'',''msdb'',''tempdb'') return
declare #startdate DATETIME = ''2019-01-01''
INSERT INTO #ReportAlexis(Companyname) Select companyname from CompanyTable where Databasename = ?;
USE ?;
INSERT INTO #ReportAlexis(TotalUsers) Select count (*) as TotalUsers from User;
INSERT INTO #ReportAlexis(UsersSinceDate) Select count (*) as UsersSinceDate from User where CreatedDate >= #startdate;
INSERT INTO #ReportAlexis(TotalAppUsers) Select count (*) as TotalAppUsers from Users where UserTypeID = 5;
INSERT INTO #ReportAlexis(AppUsersSinceDate) Select count (*) as AppUsersSinceDate from Users where UserTypeID = 5 and CreatedDate >= #startdate;
INSERT INTO #ReportAlexis(Number_of_Logins_SinceDate) Select count (*) as Number_of_Logins_SinceDate from UserLoginDetails where UserID in (Select UserID from Users where UserTypeID = 5) and LoginTime >= #startdate
'
SELECT * FROM #ReportAlexis
I assume the scope is disconnected, try a ##table (global temp table). the #table is only easily accessible within scope though the real name can be found in:
select t.name from tempdb.sys.tables t where t.name like '#ReportAlexis%'
/* it's still better to use a global temp table */
CREATE TABLE ##ReportAlexis
(
CompanyName VARCHAR(MAX),
TotalUsers INT,
UsersSinceDate INT,
TotalAppUsers INT,
AppUsersSinceDate INT,
Number_of_Logins_SinceDate INT,
);

Resources