SQL Get values from other tables before Insert Into query command - sql

I am wanting to create a query that allows me to look up a number that's presented in another table before I add that ID's name to the needed table being called by the query.
An example:
Table1
ID |name |nameID |network |...
----------------------------------
5 |Bob |4 |NPOW |...
6 |Billy |8 |BGER |...
Table2
ID |name |nameID |network |...
----------------------------------
3 |Stan |2 |ERFDT |...
Table3
ID |name |nameID |network |...
----------------------------------
3 |Steve |3 |FRVS |...
Table4 (The table I am inserting the new rows below into)
ID |name |nameID |network |...
----------------------------------
On my page these are the new values I need to add to the table4 above:
Name |ID |network |...
------------------------
Nick |4 |RFGH |...
Tony |3 |ESLO |...
James |2 |HUII |...
Rusty |3 |ERNM |...
Now what I am wanting to do is see what column ID equals X and place that name into Table4.
So doing that the outcome for table4 should look like this after the insert:
ID |name |nameFromID |network |...
--------------------------------------
1 |Nick |Bob |RFGH |...
2 |Tony |Steve |ESLO |...
3 |James |Stan |HUII |...
4 |Rusty |Steve |ERNM |...
So what would the INSERT INTO Table4 query look like for doing this?

You can do this with a view, some joins, and a temp table or some other staging table with session scoping. Here's a temp table approach.
The view to bring all the different tables into a single queryable form:
CREATE VIEW vAllNames
AS
SELECT [Name], NameID FROM Table1 UNION
SELECT [Name], NameID FROM Table2 UNION
SELECT [Name], NameID FROM Table3
The above assumes that NameId cannot be duplicated across these tables, so I've used UNION instead of UNION ALL. Otherwise you'll need to tell us how you want the name to resolve when the id matches successfully to multiple tables. Would you want multiple records inserted into table 4, only the first match, some kind of concatenation, etc.
The temp table so we have something to select from for the insertion unless you want to insert one at a time (you could also do this with a session scoped persistent table or something along those lines):
CREATE TABLE #TempDataToInsert
(
[Name] NVARCHAR(256),
ID INT,
network CHAR(4)
)
Once you've created those you can insert the data from your page directly into TempDataToInsert. Once your data is staged there you can run the final insert into:
INSERT INTO Table4
([Name], nameFromID, network) -- Assumes ID is an identity column
SELECT
tempData.[Name], allNames.[Name], tempData.network
FROM
#TempDataToInsert tempData
LEFT OUTER JOIN vAllNames allNames ON tempData.ID = allNames.NameID -- assumes no match is still valid for insertions
You'll need to clean up the temp table afterwards and I've made some assumptions about your schema but this should point you in the right direction.

CREATE TABLE #Temp0
(
ID INT,
[Name] VARCHAR(100),
NameId INT,
Network VARCHAR(100)
);
INSERT INTO #Temp0
(
ID,
Name,
NameId,
Network
)
VALUES
(5, 'Bob', 4, 'NPOW'),
(6, 'Billy', 8, 'BGER');
CREATE TABLE #Temp1
(
ID INT,
[Name] VARCHAR(100),
NameId INT,
Network VARCHAR(100)
);
INSERT INTO #Temp1
(
ID,
Name,
NameId,
Network
)
VALUES
(3, 'Stan', 2, 'ERFDT');
CREATE TABLE #Temp2
(
ID INT,
[Name] VARCHAR(100),
NameId INT,
Network VARCHAR(100)
);
INSERT INTO #Temp2
(
ID,
Name,
NameId,
Network
)
VALUES
(3, 'Steve', 3, 'FRVS');
SELECT *
INTO #Temp
FROM
(
SELECT [Name],
NameId
FROM #Temp0
UNION
SELECT [Name],
NameId
FROM #Temp1
UNION
SELECT [Name],
NameId
FROM #Temp2
) AS Temp;
CREATE TABLE #Temp3
(
[Name] VARCHAR(100),
Id INT,
Network VARCHAR(100)
);
INSERT INTO #Temp3
(
Name,
Id,
Network
)
VALUES
('Nick', 4, 'RFGH'),
('Tony', 3, 'ESLO'),
('James', 2, 'HUII'),
('RUSTY', 3, 'ERNM');
CREATE TABLE #Table4
(
ID INT IDENTITY(1, 1),
[Name] VARCHAR(100),
NameFormId VARCHAR(100),
Network VARCHAR(100)
);
INSERT INTO #Table4
SELECT T2.Name,
T1.Name,
T2.Network
FROM #Temp T1
JOIN #Temp3 T2
ON T1.NameId = T2.Id;
SELECT *
FROM #Table4;

The following code demonstrates how to match data on NameId between tables. It assumes that the Names being added are unique. If there are multiple NameId matches between input tables then the earliest match is given preference. Multiple NameId matches within a single input table give preference to the lowest Id.
-- Sample data.
declare #Table1 as Table ( Id Int, Name VarChar(10), NameId Int, Network VarChar(10) );
insert into #Table1 ( Id, Name, NameId, Network ) values
( 5, 'Bob', 4, 'NPOW' ), ( 6, 'Billy', 8, 'BGER' );
select * from #Table1;
declare #Table2 as Table ( Id Int, Name VarChar(10), NameId Int, Network VarChar(10) );
insert into #Table2( Id, Name, NameId, Network ) values
( 3, 'Stan', 2, 'ERFDT' );
select * from #Table2;
declare #Table3 as Table ( Id Int, Name VarChar(10), NameId Int, Network VarChar(10) );
insert into #Table3 ( Id, Name, NameId, Network ) values
( 3, 'Steve', 3, 'FRVS' ),
( 4, 'Steven', 3, 'FRVS' ), -- Note duplicate NameId value.
( 9, 'Stanley', 2, 'VOOT' ); -- Note duplicate NameId value.
select * from #Table3;
declare #ValuesToAdd as Table ( Name VarChar(10), NameId Int, Network VarChar(10) );
insert into #ValuesToAdd ( Name, NameId, Network ) values
( 'Nick', 4, 'RFGH' ), ( 'Tony', 3, 'ESLO' ),
( 'James', 2, 'HUII' ), ( 'Rusty', 3, 'ERNM' );
select * from #ValuesToAdd;
-- Process the data.
declare #Table4 as Table ( Id Int Identity, Name VarChar(10), NameId Int,
NameFromId VarChar(10), Network VarChar(10) );
-- Demonstrate the matching.
select Name, NameId, NameFromId, Network
from (
select VTA.Name, VTA.NameId, N.Name as NameFromId, VTA.Network,
Row_Number() over ( partition by VTA.Name order by NameRank, Id ) as RN
from #ValuesToAdd as VTA inner join ( -- Use left outer join if there might not be a match on NameId .
select Id, Name, NameId, 1 as NameRank from #Table1 union all
select Id, Name, NameId, 2 from #Table2 union all
select Id, Name, NameId, 3 from #Table3 ) as N on N.NameId = VTA.NameId ) as PH
--where RN = 1; -- Pick the match from the earliest table.
-- Insert the results.
insert into #Table4
select Name, NameId, NameFromId, Network
from (
select VTA.Name, VTA.NameId, N.Name as NameFromId, VTA.Network,
Row_Number() over ( partition by VTA.Name order by NameRank, Id ) as RN
from #ValuesToAdd as VTA inner join ( -- Use left outer join if there might not be a match on NameId .
select Id, Name, NameId, 1 as NameRank from #Table1 union all
select Id, Name, NameId, 2 from #Table2 union all
select Id, Name, NameId, 3 from #Table3 ) as N on N.NameId = VTA.NameId ) as PH
where RN = 1; -- Pick the match from the earliest table.
select * from #Table4;

Related

Need to return an ID which has start and END in sql server

I have a scenario wherein I need to find the ID which only has start and END in it. Below is the table for reference.
Declare #T Table ( ID int, Name varchar(100))
Insert into #T values (1,'Start')
Insert into #T values (1,'END')
Insert into #T values (1,'Stuart')
Insert into #T values (1,'robin')
Insert into #T values (2,'Start')
Insert into #T values (2,'END')
Insert into #T values (3,'Start')
Insert into #T values (4,'END')
I want the Output as:
ID Name
2 Start
2 END
I want those ID which only has start and end in it.
What I tried so far:
SELECT * FROM #T t
WHERE EXISTS (SELECT * FROM #T WHERE id = t.id AND name = 'start')
AND EXISTS (SELECT * FROM #T WHERE id = t.id AND name = 'END')
But my query is giving ID 1 as well.
Can someone please help me rectify the problem.
I presume your issue is that record 1 has a 'Stuart' in it too?
As such, you can do a similar check in the WHERE e.g.,
SELECT * FROM #T t
WHERE EXISTS (SELECT * FROM #T WHERE id = t.id AND name = 'start')
AND EXISTS (SELECT * FROM #T WHERE id = t.id AND name = 'END')
AND NOT EXISTS (SELECT * FROM #T WHERE id = t.id AND name NOT IN ('start','END'))
Note that you may want to consider
What happens if you have two 'start' rows or two 'end' rows (e.g., start-start-end)? Can you even have two 'start' rows (e.g., start-start)?
What happens if you have a blank/NULL (e.g., start-NULL-end)?
EDIT: removed 'What happens if they're out of order (e.g., end-start)?' as a question as there is no sorting in the data at all (e.g., not even an implicit sort).
You can go for CTE to get group wise count and total count as 2.
Declare #T Table ( ID int, Name varchar(100))
Insert into #T values (1,'Start')
Insert into #T values (1,'END')
Insert into #T values (1,'Stuart')
Insert into #T values (1,'robin')
Insert into #T values (2,'Start')
Insert into #T values (2,'END')
Insert into #T values (3,'Start')
Insert into #T values (4,'END')
;WITH CTE_Total_StartEnd AS
(
select id, count(*) AS Total_Cnt
, COUNT( case when Name IN ('Start') THEN 1 END) as start_cnt
, COUNT( case when Name IN ('End') THEN 1 END) as end_cnt
from #t
group by id
having COUNT( case when Name IN ('Start') THEN 1 END) =1 and
COUNT( case when Name IN ('End') THEN 1 END) = 1 and
count(*) = 2
)
SELECT t.* from #t t
inner join CTE_Total_StartEnd as c
ON c.id = t.id
+----+-------+
| ID | Name |
+----+-------+
| 2 | Start |
| 2 | END |
+----+-------+
You can do this by using group by function also like below
WITH cte AS
(
SELECT 1 AS id , 'Start' AS name
UNION ALL
SELECT 1 AS id ,'END' AS name
UNION ALL
SELECT 1 AS id ,'Stuart' AS name
UNION ALL
SELECT 1 AS id ,'robin' AS name
UNION ALL
SELECT 2 AS id ,'Start' AS name
UNION ALL
SELECT 2 AS id ,'END' AS name
UNION ALL
SELECT 3 AS id ,'Start' AS name
UNION ALL
SELECT 4 AS id ,'END' AS name
)
SELECT T.ID,SUM(T.VAL)AS SUM
FROM
(
SELECT id,name , CASE WHEN name='Start' THEN 1
WHEN name='END' THEN 2
ELSE 3
END AS VAL
FROM cte
)T
GROUP BY T.ID
HAVING SUM(T.VAL) =3
could you please try this? Pls note i added collate command in the end of sql.
SQL Server check case-sensitivity?
SELECT * FROM #T t
WHERE EXISTS (SELECT * FROM #T WHERE id = t.id AND name = 'start' COLLATE SQL_Latin1_General_CP1_CS_AS)
AND EXISTS (SELECT * FROM #T WHERE id = t.id AND name = 'END' COLLATE SQL_Latin1_General_CP1_CS_AS)

SQL Query second relation

i have a problem i want to take SQL query language that returns the employee's (id) supervisor to his supervisor.
I have a tab employee
create table employee (
id int not null,
surname varchar(50),
name varchar(30),
boss_id int)
You may try this...
select a.id, b.boss_id
from employee as a
inner join employee as b on a.boss_id=b.id
For test case
create table DemoTable
(
accountid bigint
,parentid bigint
,accountname nvarchar(128)
)
insert DemoTable(accountid,parentid,accountname)
select 1, null, 'Root'
union select 2, 3, 'Child1'
union select 3, 1, 'Child2'
Query:
select a.accountid, b.parentid from DemoTable as a
inner join DemoTable as b on a.parentid=b.accountid
output is as : pass accountid as 2
accountid parentid
2 1
Here 2 >> 3 >> 1

TSQL group by and combine remaining columns in json array

I want to group a result set by a column and combine the remaining columns into a json array, but I'm not sure how to aggregate the results for this.
I want the following output:
A_ID | Translations
--------------------
1 | [{"Name": "english_1","LCID": "en-gb"},{"Name": "french_1","LCID": "fr-fr"}]
2 | [{"Name": "english_2","LCID": "en-gb"},{"Name": "french_2","LCID": "fr-fr"}]
But I cannot group the results by A_ID without an aggregator so I get the following
A_ID | Translations
--------------------
1 | [{"Name": "english_1","LCID": "en-gb"}]
1 | [{"Name": "french_1","LCID": "fr-fr"}]
2 | [{"Name": "english_2","LCID": "en-gb"}]
2 | [{"Name": "french_2","LCID": "fr-fr"}]
Here is an example:
DROP TABLE IF EXISTS #tabA;
DROP TABLE IF EXISTS #tabB;
DROP TABLE IF EXISTS #tabC;
go
CREATE TABLE #tabA
(
Id int not null
);
CREATE TABLE #tabTranslations
(
translationId int not null,
Name nvarchar(32) not null,
aId int not null, -- Foreign key.
languageId int not null --Foreign key
);
CREATE TABLE #tabLanguages
(
languageId int not null,
LCID nvarchar(32) not null
);
go
INSERT INTO #tabA (Id)
VALUES
(1),
(2);
INSERT INTO #tabTranslations (translationId, Name, aId, languageId)
VALUES
(1, 'english_1', 1, 1),
(2, 'french_1', 1, 2),
(3, 'english_2', 2, 1),
(4, 'french_2', 2, 2);
INSERT INTO #tabLanguages (languageId, LCID)
VALUES
(1, 'en-gb'),
(2, 'fr-fr');
go
select
_a.Id as A_ID,
(
select
_translation.Name,
_language.LCID
for json path
)
from #tabA as _a
inner join #tabTranslations as _translation ON _translation.aId = _a.Id
inner join #tabLanguages as _language ON _language.languageId = _translation.languageId
-- group by _a.Id ??
;
go
DROP TABLE IF EXISTS #tabA;
DROP TABLE IF EXISTS #tabTranslations;
DROP TABLE IF EXISTS #tabLanguages;
go
Alternative solution:
I know I can do this, but I obviously don't want to hard code the available LCIDs (maybe I could generate the sql query and exec it? but this feels too complex), also I would prefer an array
select
_a.Id as A_ID,
(
SELECT
MAX(CASE WHEN [LCID] = 'en-gb' THEN [Name] END) 'en-gb',
MAX(CASE WHEN [LCID] = 'fr-fr' THEN [Name] END) 'fr-fr'
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
) as b
from #tabA as _a
inner join #tabTranslations as _translation ON _translation.aId = _a.Id
inner join #tabLanguages as _language ON _language.languageId = _translation.languageId
group by _a.Id;
result:
A_ID | Translations
--------------------
1 | { "en-Gb": "english_1", "fr-FR": "french_1"}
2 | { "en-Gb": "english_2", "fr-FR": "french_2"}
If I understand you correctly, next approach may help. Use additional CROSS APPLY operator and FOR JSON PATH to get your expected results:
Statement:
SELECT *
FROM #tabA AS t
CROSS APPLY (
SELECT _translation.Name AS Name, _language.LCID AS LCID
FROM #tabA _a
inner join #tabTranslations as _translation ON _translation.aId = _a.Id
inner join #tabLanguages as _language ON _language.languageId = _translation.languageId
WHERE _a.Id = t.Id
for json path
) _c(Translations)
Output:
Id Translations
1 [{"Name":"english_1","LCID":"en-gb"},{"Name":"french_1","LCID":"fr-fr"}]
2 [{"Name":"english_2","LCID":"en-gb"},{"Name":"french_2","LCID":"fr-fr"}]

SQL filter out rows with duplicate cell value

I've got a big query that's returning rows where one of the colums has duplicated values, for example:
| sysid | col1 | col2
| A | 1 | 2
| A | 2 | 3
| B | 1 | 4
sysid is the column I want to filter by, so in the above example I only want the last row of output because A appears more than once. I tried to do this to filter them out:
CREATE TABLE #temp (SysId VARCHAR(10), col1 INT);
INSERT INTO #temp VALUES ('A', 1), ('B', 1), ('A', 1);
WITH cte AS (
SELECT * FROM #temp
), sysids AS (
SELECT SysId FROM #temp GROUP BY Sysid HAVING COUNT(*) = 1
)
SELECT * FROM #temp WHERE EXISTS (SELECT SysId FROM sysids);
DROP TABLE #temp;
I expected that final select would only contain the B row, in the above example . I'm still getting all of them though, and I don't understand why.
Obviously in this specific example I don't need the cte part but my real query is pretty complex with multiple unions.
You can use a correlated, aggregating subyquery and NOT EXISTS.
SELECT t1.*
FROM #temp t1
WHERE NOT EXISTS (SELECT ''
FROM #temp t2
WHERE t2.sysid = t1.sysid
HAVING count(*) > 1);
Please try this:
;WITH cte AS (
SELECT t.SysId,t.col1,COUNT(1)OVER(PARTITION BY t.SysID) AS [cnt]
FROM #temp t
)
SELECT c.SysId,c.col1
FROM cte c WHERE c.cnt = 1;
CREATE TABLE #temp (SysId VARCHAR(10), col1 INT);
INSERT INTO #temp VALUES ('A', 1), ('B', 1), ('A', 1);
WITH cte AS (
SELECT * FROM #temp
), sysids AS (
SELECT SysId FROM #temp GROUP BY Sysid HAVING COUNT(SysId) =1
)
SELECT * FROM #temp WHERE SysId IN (SELECT SysId FROM sysids);
DROP TABLE #temp;

SQL: Upsert and get the old and the new values

I have the following table Items:
Id MemberId MemberGuid ExpiryYear Hash
---------------------------------------------------------------------------
1 1 Guid1 2017 Hash1
2 1 Guid2 2018 Hash2
3 2 Guid3 2020 Hash3
4 2 Guid4 2017 Hash1
I need to copy the items from a member to another (not just to update MemberId, to insert a new record). The rule is: if I want to migrate all the items from a member to another, I will have to check that that item does not exists in the new member.
For example, if I want to move the items from member 1 to member 2, I will move only item with id 2, because I already have an item at member 2 with the same hash and with the same expiry year (this are the columns that I need to check before inserting the new items).
How to write a query that migrates only the non-existing items from a member to another and get the old id and the new id of the records? Somehow with an upsert?
You can as the below:
-- MOCK DATA
DECLARE #Tbl TABLE
(
Id INT IDENTITY NOT NULL PRIMARY KEY,
MemberId INT,
MemberGuid CHAR(5),
ExpiryYear CHAR(4),
Hash CHAR(5)
)
INSERT INTO #Tbl
VALUES
(1, 'Guid1', '2017', 'Hash1'),
(1, 'Guid2', '2018', 'Hash1'),
(2, 'Guid3', '2020', 'Hash3'),
(2, 'Guid4', '2017', 'Hash1')
-- MOCK DATA
-- Parameters
DECLARE #FromParam INT = 1
DECLARE #ToParam INT = 2
DECLARE #TmpTable TABLE (NewDataId INT, OldDataId INT)
MERGE #Tbl AS T
USING
(
SELECT * FROM #Tbl
WHERE MemberId = #FromParam
) AS F
ON T.Hash = F.Hash AND
T.ExpiryYear = F.ExpiryYear AND
T.MemberId = #ToParam
WHEN NOT MATCHED THEN
INSERT ( MemberId, MemberGuid, ExpiryYear, Hash)
VALUES ( #ToParam, F.MemberGuid, F.ExpiryYear, F.Hash)
OUTPUT inserted.Id, F.Id INTO #TmpTable;
SELECT * FROM #TmpTable
Step 1:
Get in cursor all the data of member 1
Step 2:
While moving through cursor.
Begin
select hash, expirydate from items where memberid=2 and hash=member1.hash and expirydate=member1.expirydate
Step 3
If above brings any result, do not insert.
else insert.
Hope this helps
Note: this is not actual code. I am providing you just steps based on which you can write sql.
Actually you just need an insert. When ExpiryYear and Hash matched you don't wanna do anything. You just wanna insert from source to target where those columns doesn't match. You can do that with Merge or Insert.
CREATE TABLE YourTable
(
Oldid INT,
OldMemberId INT,
Id INT,
MemberId INT,
MemberGuid CHAR(5),
ExpiryYear CHAR(4),
Hash CHAR(5)
)
INSERT INTO YourTable VALUES
(null, null, 1, 1, 'Guid1', '2017', 'Hash1'),
(null, null, 2, 1, 'Guid2', '2018', 'Hash2'),
(null, null, 3, 2, 'Guid3', '2020', 'Hash3'),
(null, null, 4, 2, 'Guid4', '2017', 'Hash1')
DECLARE #SourceMemberID AS INT = 1
DECLARE #TargetMemberID AS INT = 2
MERGE [YourTable] AS t
USING
(
SELECT * FROM [YourTable]
WHERE MemberId = #SourceMemberID
) AS s
ON t.Hash = s.Hash AND t.ExpiryYear = s.ExpiryYear AND t.MemberId = #TargetMemberID
WHEN NOT MATCHED THEN
INSERT(Oldid, OldMemberId, Id, MemberId, MemberGuid, ExpiryYear, Hash) VALUES (s.Id, s.MemberId, (SELECT MAX(Id) + 1 FROM [YourTable]), #TargetMemberID, s.MemberGuid, s.ExpiryYear, s.Hash);
SELECT * FROM YourTable
DROP TABLE YourTable
/* Output:
Oldid OldMemberId Id MemberId MemberGuid ExpiryYear Hash
-----------------------------------------------------------------
NULL NULL 1 1 Guid1 2017 Hash1
NULL NULL 2 1 Guid2 2018 Hash2
NULL NULL 3 2 Guid3 2020 Hash3
NULL NULL 4 2 Guid4 2017 Hash1
2 1 5 2 Guid2 2018 Hash2
If you just want to select then do as following
SELECT null AS OldID, null AS OldMemberID, Id, MemberId, MemberGuid, ExpiryYear, Hash FROM YourTable
UNION ALL
SELECT A.Id AS OldID, A.MemberId AS OldMemberID, (SELECT MAX(Id) + 1 FROM YourTable) AS Id, #TargetMemberID AS MemberId, A.MemberGuid, A.ExpiryYear, A.Hash
FROM YourTable A
LEFT JOIN
(
SELECT * FROM YourTable WHERE MemberId = #TargetMemberID
) B ON A.ExpiryYear = B.ExpiryYear AND A.Hash = B.Hash
WHERE A.MemberId = #SourceMemberID AND B.Id IS NULL