Join contents of field and insert new record - sql

I have two tables in the following structure
Table - MemoType
ID | MemoTypeID | MemoTypeName
1 1234 A
2 5678 B
Table - Memos
ID | MemoTypeID | Memo | ExtRef
1 1234 TextOne XYZ
2 5678 TextTwo XYZ
3 1234 TextThree TUV
We would like to update these tables to reflect the following data
Table - MemoType
ID | MemoTypeID | MemoTypeName
3 9999 NewCombinedMemo
Table - Memos
ID | MemoTypeID | Memo | ExtRef
4 9999 <A> TextOne <B> TextTwo XYZ
5 9999 <A> TextThree TUV
The memos table has about 2 million rows with about 200,000 unique values for ExtRef.
My thinking is along the following lines (using .NET): Populate a List of all unique ExtRef values from Memos table; For each unique ExtRef get a list of all Memo values; concatenate strings as required; insert new record for each ExtRef; delete rest of the records for each ExtRef. The problem is that this would result in a large number of sql operations.
Please suggest if there are other efficient strategies to achieve this directly in SQL.

This is indeed possible directly through SQL, the following creates table variables to demonstrate / test with sample data and doesn't delete the original data.
The orginal data could easily be deleted using a clause checking on the memo type id, but I'd want to hold off on that until I'd performed a manual check on such a large table!
-- setting the scene
DECLARE #MemoType TABLE
(
Id int,
MemoTypeId int,
MemoTypeName varchar(30)
)
DECLARE #Memo TABLE
(
Id int identity(1,1),
MemoTypeId int,
Memo varchar(500),
ExtRef varchar(1000)
)
INSERT INTO #MemoType VALUES (1,1234,'A');
INSERT INTO #MemoType VALUES (2,1234,'B');
INSERT INTO #MemoType VALUES (3,9999,'NewCombinedMemo');
INSERT INTO #Memo VALUES (1234, 'TextOne', 'XYZ');
INSERT INTO #Memo VALUES (5678, 'TextTwo', 'XYZ');
INSERT INTO #Memo VALUES (1234, 'TextThree', 'TUV');
WITH cte(id, memotype, memotext, ref) as (
SELECT Id, MemoTypeId, Memo, ExtRef FROM #Memo
)
INSERT INTO #memo
SELECT 9999, stuff(memos,1,1,''),ref
FROM cte [outer]
CROSS APPLY (
SELECT ',' + memotext
FROM cte [inner]
WHERE [outer].ref = [inner].ref
FOR XML PATH('')
) n(memos)
GROUP BY ref, memos
select * from #memo
The CTE logic/description was borrowed from string concatenate in group by function with other aggregate functions - adding in logic to insert and strip out the leading comma.
I placed your original query in a CTE.
Then I cross applied with a subquery that gets
a comma-delimited set of memos for each reference in the outer query.
Since I also selected the memos column, I had to also group by the
memos column.
An initial comma needed to be stripped out with the stuff function
Finally, the result is inserted.

Related

Insert from select on same table and store old and new identity values in separate table

Temporary table:
declare #Temp_Table table
(
newSID int,
oldSID int
)
Database table: Solutions
Solutions:
* 1 solution1 111
* 2 solution2 111
* 3 solution3 111
After insert,
* 1 solution1 111
* 2 solution2 111
* 3 solution3 111
* 4 solution1 222
* 5 solution2 222
* 6 solution3 222
temp table Expected
oldsID NewSID
* 1 4
* 2 5
* 3 6
This table has SID (identity), SName and cnumber.
Now I want to select some rows from the Solutions table and insert their values into same table.
When inserting each row I want to store the old identity value and new identity value in the temporary table (#Temp_Table).
Please help me with this.
The trick is to use merge instead of a regular insert into..select, since with merge you can use data from both the source and the target in the output clause.
First, create and populate sample table (Please save us this step in your future questions):
CREATE TABLE Solutions
(
SolutionID int identity (1,1),
SolutionName varchar(10),
ClientNumber int
)
INSERT INTO Solutions (SolutionName, ClientNumber) VALUES
('solution1', 111),
('solution2', 111),
('solution3', 111)
Then, declare the mapping table:
DECLARE #Temp_MasterSolutionsTable AS TABLE
(
newSolutionID int,
oldSolutionID int
)
Next, Copy the records you want:
MERGE INTO Solutions USING
(
SELECT SolutionID, SolutionName, ClientNumber
FROM Solutions
--WHERE CONDITION -- I'm guessing you will need a where clause here
) AS s ON 1 = 0 -- Always not matched
WHEN NOT MATCHED THEN
INSERT (SolutionName, ClientNumber)
VALUES (s.SolutionName, s.ClientNumber)
-- and here is where the magic happens
OUTPUT Inserted.SolutionID, s.SolutionID
INTO #Temp_MasterSolutionsTable (newSolutionID, oldSolutionID);
See a live demo on rextester.

Inserting rows into TargetTable, comparing rows from a select in a new table

I'm working on a stored procedure, and it's not working as I would expect. Looking for a fresh set of eyes to see what I am missing. I am populating a table with some distinct values, and if those values don't exist in the table I'm inserting into, then do it. Don't want dupes. The list of values has rows, but it is not inserting into the target table.
declare #DistinctTable table (tableName varchar(30))
insert into #DistinctTable (tableName) (SELECT DISTINCT Reference FROM dbo.Tmp_Import_Rows)
INSERT INTO [dbo].[TargetTable]
(RowID
,A
,B
,C
,D
,E
,F
,G
,User
,Machine
,Date
,TableRef)
(SELECT NEWID(),
0,
0,
0,
0,
0,
0,
0,
#pUserName,
HOST_NAME(),
getdate(),
tableName
FROM #DistinctTable
WHERE (tableName NOT IN (select [TableRef] from [TargetTable] )))
As I said, I'm getting rows into #DistinctTable, but they won't insert into [TargetTable]. I feel like I'm overlooking something small. Any advice would be great, thank you.
Edit: Sample data
dbo.TargetTable
RowId, TableReferecne| Inches | Volume | Unused | Unused | Unused | Unused | UserName | MACHINE | DATE | Table#
897e0a1f-f139-4c1d-9bac-0a64518cd56a, Table50, 12,20,0,0,0,0,someuser, somemachine, 4-24-15, Table50
dbo.Tmp_Import_Rows
TableReference | Inches| Volume | Table #
Table60 | 60 | 1000 | Table 60
Table70 | 70 | 1100 | Table 70
#DistinctTable is populated with unique values, On [Table #]
tableName
Table60
Table70
What I want it to do, is Get Distinct values from Tmp_Import_rows, and if those values don't exist in TargetTable, Insert it.
Try WHERE NOT EXISTS instead of NOT IN and see if you get anything in return:
SELECT *
FROM #DistinctTable t1
WHERE NOT EXISTS (
SELECT 1
FROM [dbo].[TargetTable] t2
WHERE t1.tableName = t2.tableRef
);
Don't you need the keyword VALUES in there?
e.g. insert into table (col1,col2,col3...) values (val1,val2,val3...) etc?

T-SQL Insert Column from Other Table

Hello I am trying to insert values from one table column to another table. I am using SQL Server 2008 R2. Here is example:
Table 1
Declare #Table1 table (file varchar(15), type int, partiesid int)
Table 2
Declare #Table2 table (file varchar(15), ....many other columns)
I would like to insert file from Table 2 into Table 1 as well as some other static values.
So select * from table1 would end up looking like this:
File Type PartiesID
GW100 1 555
GW101 1 555
GW103 1 555
GW104 1 555
where the GW100, GW101, etc come from table2 and the 1 and 555 are static for every row.
I tried insert into table1 (file,type,partiesid) values (select file from table2,1,555) but that doesn't work. Is there a way where it will just insert a row for each unique file and also insert the static fields of 1 and 555 as separate columns?
Thanks!
Insert into #table2
(
file,
type,
partiesid,
...(other columns for which you need to give static values)
)
select
file,
type,
partiesid,
...(static values for columns)
from #table1
You can try the below query:
USE dbName
GO
INSERT INTO table2 (column_name(s))
SELECT column_name(s) FROM table1;
GO

In a persisted field, how do you return the number of occurrences of a column within a different table's column

The following is required due to records being entered by 3rd parties in a web application.
Certain columns (such as Category) require validation including the one below. I have a table OtherTable with the allowed values.
I need to identify how many occurrences (ie: IF) there are of the current table's column's value in a different table's specified column. If there are no occurrences this results in a flagged error '1', if there are occurrences, then it results in no flagged error '0'.
If `Category` can be found in `OtherTable.ColumnA` then return 0 else 1
How can I do this please?
If Category can be found in OtherTable.ColumnA then return 0 else 1
You could use CASE with EXISTS
SELECT CASE WHEN EXISTS(
SELECT NULL
FROM AllowedValues av
WHERE av.ColumnA = Category
) THEN 0 ELSE 1 END AS ErrorCode
, Category
FROM [Table]
Edit: Here's a sql-fiddle: http://sqlfiddle.com/#!3/55a2e/1
Edit: I've only just noticed that you want to use a computed column. As i've read you can only use it with scalar values and not with sub-queries. But you can create a scalar valued function.
For example:
create table AllowedValues(ColumnA varchar(1));
insert into AllowedValues Values('A');
insert into AllowedValues Values('B');
insert into AllowedValues Values('C');
create table [Table](Category varchar(1));
insert into [Table] Values('A');
insert into [Table] Values('B');
insert into [Table] Values('C');
insert into [Table] Values('D');
insert into [Table] Values('E');
-- create a scalar valued function to return your error-code
CREATE FUNCTION udf_Category_ErrorCode
(
#category VARCHAR(1)
)
RETURNS INT
AS BEGIN
DECLARE #retValue INT
SELECT #retValue =
CASE WHEN EXISTS(
SELECT NULL
FROM AllowedValues av
WHERE av.ColumnA = #category
) THEN 0 ELSE 1 END
RETURN #retValue
END
GO
Now you can add the column as computed column which uses the function to calculate the value:
ALTER TABLE [Table] ADD ErrorCode AS ( dbo.udf_Category_ErrorCode(Category) )
GO
Here's the running SQL: http://sqlfiddle.com/#!3/fc49e/2
Note: as #Damien_The_Unbelieve has commented at the other answer, even if you persist the result with a UDF, the value won't be updated if the rows in OtherTable change. Just keep that in mind, so you need to update the table manually if desired with the help of the UDF.
select mt.*,IFNULL(cat_count.ct,0) as Occurrences from MainTable mt
left outer join (select ColumnA,count(*) as ct from OtherTable) cat_count
on mt.Category=cat_count.ColumnA
Result:
mt.col1 | mt.col2 | Category | Occurrences
### | ### | XXX | 3
### | ### | YYY | 0
### | ### | ZZZ | 1

SQL Query to return rows where a list of numbers is between start and end values

There is a table in Oracle with the columns:
id | start_number | end_number
---+--------------+------------
1 | 100 | 200
2 | 151 | 200
3 | 25 | 49
4 | 98 | 99
5 | 49 | 100
There is a list of numbers (50, 99, 150).
I want an sql statement that returns all the ids where any of the numbers in the list of numbers is found equal to or between the start_number and the end_number.
Using the above example; 1, 4 and 5 should be returned.
1 - 150 is between or equal to 100 and 200
2 - none of the numbers are between or equal to 151 and 200
3 - none of the numbers are between or equal to 25 and 49
4 - 99 is between or equal to 98 and 99
5 - 50 and 99 are between or equal to 49 and 100
drop table TEMP_TABLE;
create table TEMP_TABLE(
THE_ID number,
THE_START number,
THE_END number
);
insert into TEMP_TABLE(THE_ID, THE_START, THE_END) values (1, 100, 200);
insert into TEMP_TABLE(THE_ID, THE_START, THE_END) values (2, 151, 200);
insert into TEMP_TABLE(THE_ID, THE_START, THE_END) values (3, 25, 49);
insert into TEMP_TABLE(THE_ID, THE_START, THE_END) values (4, 98, 99);
insert into TEMP_TABLE(the_id, the_start, the_end) values (5, 49, 100);
The following is the solution I came up with based on the comments and answers below plus some additional research:
SELECT
*
from
TEMP_TABLE
where
EXISTS (select * from(
select column_value as id
from table(SYS.DBMS_DEBUG_VC2COLL(50,99,150))
)
where id
BETWEEN TEMP_TABLE.the_start AND TEMP_TABLE.the_end
)
This works too:
SELECT
*
from
TEMP_TABLE
where
EXISTS (select * from(
select column_value as id
from table(sys.ku$_vcnt(50,99,150))
)
where id
BETWEEN TEMP_TABLE.the_start AND TEMP_TABLE.the_end
)
Here is a full example:
create table #list (
number int
)
create table #table (
id int,
start_number int,
end_number int
)
insert into #list values(50)
insert into #list values(99)
insert into #list values(150)
insert into #table values(1,100,200)
insert into #table values(2,151,200)
insert into #table values(3,25,49)
insert into #table values(4,98,99)
insert into #table values(5,49,100)
select distinct a.* from #table a
inner join #list l --your list of numbers
on l.number between a.start_number and a.end_number
drop table #list
drop table #table
You'll simply need to remove the code about #table (create, insert and drop) and put your table in the select.
It partly depends on how your are storing your list of numbers. I'll assume that they're in another table for now, as even then you have many options.
SELECT
*
FROM
yourTable
WHERE
EXISTS (SELECT * FROM yourList WHERE number BETWEEN yourTable.start_number AND yourTable.end_number)
Or...
SELECT
*
FROM
yourTable
INNER JOIN
yourList
ON yourList.number BETWEEN yourTable.start_number AND yourTable.end_number
Both of those are the simplest expressions, and work well for small data sets. If your list of numbers is relatively small, and your original data is relatively large, however, this may not scale well. This is because both of the above scan the whole of yourTable and then check each record against yourList.
What may be preferable is to scan the list, and then attempt to use indexes to check against the original data. This would require you to be able to reverse the BETWEEN statement to yourTable.start_number BETWEEN x and y
This can only be done if you know the maximum gap between start_number and end_number.
SELECT
*
FROM
yourList
INNER JOIN
yourTable
ON yourTable.end_number >= yourList.number
AND yourTable.start_number <= yourList.number
AND yourTable.start_number >= yourList.number - max_gap
To achieve this I would store the value of max_gap in another table, and update it as the values in yourTable change.
You will want to create a temporary table to hold your numbers, if the numbers aren't already in one. Then it becomes relatively simple:
SELECT DISTINCT mt.ID FROM MyTable mt
INNER JOIN TempTable tt --your list of numbers
ON tt.number Between mt.start_number and mt.end_number
To create the table based on an array of passed values, you can use table definitions in your procedure. I'm light on Oracle syntax and don't have TOAD handy, but you should be able to get something like this to work:
CREATE OR REPLACE PROCEDURE FindIdsFromList
AS
DECLARE
TYPE NumberRecord IS RECORD (Number int NOT NULL)
TYPE NumberList IS TABLE OF NumberRecord;
NumberList myNumberList;
BEGIN
myNumberList := (50,99,150);
SELECT DISTINCT mt.ID FROM MyTable mt
INNER JOIN myNumberList nt --your list of numbers
ON nt.Number Between mt.start_number and mt.end_number
END