I have been able to find similar issues to one I am having, but not that helps me to resolve!
I have a table that contains job data (tablea), schema as follows:
CREATE TABLE [dbo].[tablea](
[ID] [int] IDENTITY(1,1) NOT NULL,
[JobNo] [varchar](32) NOT NULL,
[JobDesc] [varchar](255) NULL,
[RequiredDate] [datetime] NULL
Certain jobs within this table provide components for other jobs in the same table, going forward I will call the jobs that provide components "Production Jobs" and the job receiving the components "sub jobs" - there is nothing within the table that links the rows.
There is another table (tableb)
CREATE TABLE [dbo].[tableb](
[ID] [int] IDENTITY(1,1) NOT NULL,
[JobNo] [varchar](32) NOT NULL CONSTRAINT [DF_JobProducts_JobNo] DEFAULT (''),
[SubJobNo] [varchar](32) NULL
This contains the "production job" job number (tableb.JobNo), and in the same row, the job number of the "sub job" (tableb.SubJobNo)
nb if a "production job" provides components for 7 x "sub jobs" there will be 7 x rows in tableb
I have an objective to execute a stored procedure that will update the required date column of the "production job" row of tablea with the EARLIEST date of the required date column of the associated "sub job". When executing the stored procedure, a unique identifier will be used as the variable - this will be the title of the "production job".
My attempt is as follows:
CREATE PROCEDURE [dbo].[UpdateDate]
#Title varchar(32),
AS
BEGIN
SET NOCOUNT ON;
UPDATE ta1
SET ta1.RequiredDate = ta2.RequiredDate
FROM tablea ta1
INNER JOIN tableb tb ON ta1.JobNo = tb.JobNo
INNER JOIN
(SELECT JobNo, MIN(RequiredTime)
GROUP BY JobNo
FROM tablea) ta2 ON tb.SubJobNo = ta2.JobNo
WHERE ta1.JobDesc = #Title
This has got me stumped so any help or pointers would be greatly appreciated!
If you are trying to say that tabalea relates to tableb then back to tablea to get to required time then just change the cte to something like this:
CREATE PROCEDURE [dbo].[UpdateDate]
#Title varchar(32),
AS
BEGIN
SET NOCOUNT ON;
;WITH cte AS (
SELECT
ta.JobNo
,MIN(ta2.RequiredTime) as NewRequiredDate
FROM
tablea ta
INNER JOIN tableb tb
ON ta.JobNo = tb.JobNo
INNER JOIN tablea ta2
ON tb.SubJobNo = ta2.JobNo
WHERE
ta.JobDesc = #Title
GROUP BY
ta.JobNo
)
UPDATE ta
SET RequiredDate = c.NewRequiredDate
FROM tablea ta
INNER JOIN cte c
ON ta.JobNo = c.JobNo
Related
I am developing a stored procedure in SQL that receives a list of Ids (INT) from the backend. I need to dynamically create columns based on these and give the columns for each ID a name. Below is an example of how to do this with a fixed number of known Ids
SELECT DISTINCT
SE.SE_optionCode AS OptionCode,
LTRIM(RTRIM(DO.DO_Description)) AS Description,
LTRIM(RTRIM(CASE WHEN DG.DG_Description IS NULL THEN '' ELSE DG.DG_Description END)) AS GenericDescription,
LTRIM(RTRIM(CASE WHEN DG.DG_GenericCode IS NULL THEN 0 ELSE DG.DG_GenericCode END)) as GenericCode,
DCS.DC_CatCode AS Category,
LTRIM(RTRIM(DCS.DC_Description)) AS Category,
DCG.DC_CatCode AS GenericCategory,
LTRIM(RTRIM(DCG.DC_Description)) AS GenericCategory,
(SELECT DISTINCT 1 FROM NVDStandardEquipment WHERE SE_id = 93762 AND SE_optionCode = SE.SE_optionCode AND SE.SE_effectiveTo IS NULL) as Vehicle1,
(SELECT DISTINCT 1 FROM NVDStandardEquipment WHERE SE_id = 93786 AND SE_optionCode = SE.SE_optionCode AND SE.SE_effectiveTo IS NULL) as Vehicle2,
(SELECT DISTINCT 1 FROM NVDStandardEquipment WHERE SE_id = 93787 AND SE_optionCode = SE.SE_optionCode AND SE.SE_effectiveTo IS NULL) as Vehicle3,
(SELECT DISTINCT 1 FROM NVDStandardEquipment WHERE SE_id = 93801 AND SE_optionCode = SE.SE_optionCode AND SE.SE_effectiveTo IS NULL) as Vehicle4
FROM NVDStandardEquipment SE (NOLOCK)
INNER JOIN NVDDictionaryOption DO (NOLOCK) ON DO.DO_OptionCode = SE.SE_optionCode
INNER JOIN NVDDictionaryOptionGenericCatLink DOGCL (NOLOCK) ON DOGCL.OGCL_OptionCatCode = DO.DO_CatCode
LEFT JOIN NVDDictionaryOptionGenericLink DOGL ON DOGL.ogl_optioncode = SE.SE_optionCode
LEFT JOIN NVDDictionaryGeneric DG ON DG.DG_GenericCode = DOGL.OGL_genericCode
INNER JOIN NVDDictionaryCategory DCS (NOLOCK) ON DCS.DC_CatCode = DO.DO_CatCode
INNER JOIN NVDDictionaryCategory DCG (NOLOCK) ON DCG.DC_CatCode = DOGCL.OGCL_GenericCatCode
WHERE SE.SE_id in (93762,93786,93787,93801)
This will return a result set as in the attached image.
In my stored procedure I am declaring a temporary table where I store all the Ids received from the backend, somehow I would have to loop through these (Not sure if thats the best approach), I don't know whether is possible to generate the same output based on a ID list to generate the columns as per the example, has anyone done something similar or have some ideas about how I could achieve this?
Thanks in advance!
Let's say you need to update/maintain the following table.
CREATE TABLE [dbo].[Test1](
[MemberID] [varchar](50) NULL,
[InpatientDays] [varchar](50) NULL,
[ERVisits] [varchar](50) NULL,
[OfficeVisits] [varchar](50) NULL,
[Narcotics] [varchar](50) NULL,
[DaysSinceLastERVisit] [varchar](50) NULL,
[Pain] [varchar](50) NULL,
[TotalVisits] [varchar](50) NULL
) ON [PRIMARY]
GO
You have a file with a list of the following IDs.
AcuteDrugGapSmall
ClaimLines
MedicalClaims
PoorCare
ProviderCount
StartedOnCombination
Create a table to save the IDs.
CREATE TABLE [dbo].[Test2](
[ID] [varchar](50) NULL
) ON [PRIMARY]
GO
After you insert the IDs run the following to generate the TSQL script to add the new columns. I'm assuming they're all VARCHAR(50) in this example. You can tweak to code given more information regarding data_type and character_maximum_length. You can take a similar approach to drop columns if needed. If you had a flag in table Test2 indicating whether to add or drop the column.
DECLARE #SQL VARCHAR(MAX)
SELECT #SQL = STRING_AGG('ALTER TABLE [dbo].[Test1] ADD ' + ID + ' VARCHAR(50)', ' ')
FROM [dbo].[Test3]
EXECUTEÂ (#SQL)
The above script generates and executes the following. Please mark as solution if this works for you.
ALTER TABLE [dbo].[Test1] ADD AcuteDrugGapSmallVARCHAR(50)
ALTER TABLE [dbo].[Test1] ADD ClaimLinesVARCHAR(50)
ALTER TABLE [dbo].[Test1] ADD MedicalClaimsVARCHAR(50)
ALTER TABLE [dbo].[Test1] ADD PoorCareVARCHAR(50)
ALTER TABLE [dbo].[Test1] ADD ProviderCountVARCHAR(50)
ALTER TABLE [dbo].[Test1] ADD StartedOnCombinationVARCHAR(50)
I'm trying to write a stored procedure to pull information from an XML string and use it to create multiple parent-child relationships. I am trying to push this XML into actual database tables. Basically, the local client will send an XML file to the database and store it as a string. I then need to pull the information out of that string and update the appropriate tables. If this was just a Table-A to Table-B, this wouldn't be so difficult. The problem I'm running into is it need to go from Table-A to Table-B to Table-C to Table-D where applicable. Below is a sample XML:
<RunRecordFile>
<Competition>
<Name>Daily</Name>
<StartDate>11/9/2015 12:40:07 AM</StartDate>
<Runs>
<Id>123</Id>
<Name>Daily Run</Name>
<RunDate>11/9/2015 12:40:07 AM</RunDate>
<CompetitionId>1</CompetitionId>
<RunRecords>
<Id>001</Id>
<Number>007</Number>
<ElapsedTime>23.007</ElapsedTime>
<RunId>123</RunId>
</RunRecords>
</Runs>
<Runs>
<Id>456</Id>
<Name>Daily Run</Name>
<RunDate>11/9/2015 12:47:07 AM</RunDate>
<CompetitionId>1</CompetitionId>
<RunRecords>
<Id>002</Id>
<Number>700</Number>
<ElapsedTime>23.707</ElapsedTime>
<RunId>456</RunId>
<RunRecordSpecialty>
<Id>1</Id>
<Handicap>17</Handicap>
<TeamPoints>50000</TeamPoints>
<RunRecordId>002</RunRecordId>
</RunRecordSpecialty>
</RunRecords>
</Runs>
</Competition>
</RunRecordFile>
I've attempted to use a DECLARED table to hold each of the created Primary Keys and to use SQL OUTPUT in order to gather those. When I run my SQL I'm getting (0) Rows Updated. Here's what I've tried in SQL:
CREATE PROC [dbo].[RaceFilePush]
AS
DECLARE #CompetitionIdMapping TABLE ( CompetitionId bigint )
DECLARE #RunIdMapping TABLE ( RunId bigint )
DECLARE #RunRecordIdMapping TABLE ( RunRecordId bigint )
BEGIN
DECLARE #rrXML AS XML
DECLARE #rrfId AS BIGINT
SET #rrfId = (SELECT TOP 1 Id FROM RunRecordFile WHERE Submitted IS NULL)
SET #rrXML = (SELECT TOP 1 RaceFile FROM RunRecordFile WHERE Id = #rrfId)
BEGIN TRAN Competitions
BEGIN TRY
INSERT INTO Competition (
Name
,StartDate
)
OUTPUT INSERTED.Id INTO #CompetitionIdMapping(CompetitionId)
SELECT
xCompetition.value('(Name)[1]', 'varchar(225)') AS Name
,xCompetition.value('(StartDate)[1]', 'datetime') AS StartDate
,#rrfId AS RunRecordFileId
FROM
#rrXML.nodes('/RunRecordFile/Competition') AS E(xCompetition)
INSERT INTO Run (
Name
,RunDate
,CompetitionId
)
OUTPUT INSERTED.Id INTO #RunIdMapping(RunId)
SELECT
xRuns.value('(Name)[1]','varchar(80)') AS Name
,xRuns.value('(RunDate)[1]','datetime') AS RunDate
,(SELECT CompetitionId FROM #CompetitionIdMapping)
FROM
#rrXML.nodes('/RunRecordFile/Competition/Runs') AS E(xRuns)
INSERT INTO RunRecord (
Number
,ElapsedTime
,RunId
)
OUTPUT INSERTED.Id INTO #RunRecordIdMapping(RunRecordId)
SELECT
xRunRecords.value('(Number)[1]','varchar(10)') AS Number
,xRunRecords.value('(ElapsedTime)[1]','numeric(10,5)') AS ElapsedTime
,(SELECT RunId FROM #RunIdMapping)
FROM
#rrXML.nodes('/RunRecordFile/Competition/Runs/RunRecords') AS E(xRunRecords)
INSERT INTO RunRecordSpecialty (
Handicap
,TeamPoints
,RunRecordId
)
SELECT
xRunRecordSpecialty.value('(Handicap)[1]','numeric(10,5)') AS Handicap
,xRunRecordSpecialty.value('(TeamPoints)[1]','numeric(10,5)') AS TeamPoints
,(SELECT RunRecordId FROM #RunRecordIdMapping)
FROM
#rrXML.nodes('/RunRecordFile/Competition/Runs/RunRecordSpecialty') AS E(xRunRecordSpecialty)
UPDATE RunRecordFile SET Submitted = GETDATE() WHERE Id = #rrfId
COMMIT TRAN Competitions
END TRY
BEGIN CATCH
ROLLBACK TRAN Competitions
END CATCH
END
With this SQL you get the whole thing into a flat declared table #tbl:
Remark: I placed the XML from your question into a variable called #xml. Adapt this to your needs...
DECLARE #tbl TABLE (
[Competition_Name] [varchar](max) NULL,
[Competition_StartDate] [datetime] NULL,
[Run_Id] [int] NULL,
[Run_Name] [varchar](max) NULL,
[Run_RunDate] [datetime] NULL,
[Run_CompetitionId] [int] NULL,
[RunRecords_Id] [int] NULL,
[RunRecords_Number] [int] NULL,
[RunRecords_ElapsedTime] [float] NULL,
[RunRecords_RunId] [int] NULL,
[RunRecordSpecialty_Id] [int] NULL,
[RunRecordSpecialty_Handicap] [int] NULL,
[RunRecordSpecialty_TeamPoints] [int] NULL,
[RunRecordSpecialty_RunRecordId] [int] NULL
);
INSERT INTO #tbl
SELECT Competition.value('Name[1]','varchar(max)') AS Competition_Name
,Competition.value('StartDate[1]','datetime') AS Competition_StartDate
,Run.value('Id[1]','int') AS Run_Id
,Run.value('Name[1]','varchar(max)') AS Run_Name
,Run.value('RunDate[1]','datetime') AS Run_RunDate
,Run.value('CompetitionId[1]','int') AS Run_CompetitionId
,RunRecords.value('Id[1]','int') AS RunRecords_Id
,RunRecords.value('Number[1]','int') AS RunRecords_Number
,RunRecords.value('ElapsedTime[1]','float') AS RunRecords_ElapsedTime
,RunRecords.value('RunId[1]','int') AS RunRecords_RunId
,RunRecordSpecialty.value('Id[1]','int') AS RunRecordSpecialty_Id
,RunRecordSpecialty.value('Handicap[1]','int') AS RunRecordSpecialty_Handicap
,RunRecordSpecialty.value('TeamPoints[1]','int') AS RunRecordSpecialty_TeamPoints
,RunRecordSpecialty.value('RunRecordId[1]','int') AS RunRecordSpecialty_RunRecordId
FROM #xml.nodes('/RunRecordFile/Competition') AS A(Competition)
OUTER APPLY Competition.nodes('Runs') AS B(Run)
OUTER APPLY Run.nodes('RunRecords') AS C(RunRecords)
OUTER APPLY RunRecords.nodes('RunRecordSpecialty') AS D(RunRecordSpecialty)
;
SELECT * FROM #tbl
If you need generated IDs, you just add the columns to #tbl and write them there, either "on the flow" or afterwards wiht UPDATE statement.
It should be easy to work through this flat table, select just the needed data level with DISTINCT and insert the rows, then the next level and so on...
Good luck!
I have a SQL Server database, of which I need to select the remaining rows from two views.
The thought behind it is this: I store game divisions in one table, and clans in another.
The vwGetGameDivisions gets all the games that can be subjected to a division.
The vwGetClanDivisions gets all the current game divisions the clan is subscribed to.
Currently, using
SELECT dbo.vwGetGameDivisions.name, dbo.vwGetClanDivisions.clanName
FROM dbo.vwGetClanDivisions
RIGHT OUTER JOIN
dbo.vwGetGameDivisions
ON dbo.vwGetClanDivisions.gameName = dbo.vwGetGameDivisions.name
Gets all the 'divisions', and 'clans' registered to them.
I want to display the remaining divisions (the rest that a certain clan isn't registered to), so I can bind it to a dropDownList for a clan to see what they can still subscribe to.
I am still quite a newbee on SQL queries, and don't even know how to go about this..
I've tried WHERE (dbo.vwGetClanDivisions.clanName = NULL), but that only returns divisions that have no clan at all.
EDIT - structures:
CREATE TABLE [dbo].[tblSiteClanGameDivision](
[id] [int] IDENTITY(1,1) NOT NULL,
[clanId] [int] NOT NULL,
[gameId] [int] NOT NULL,
[removed] [tinyint] NOT NULL,
[dateAdded] [datetime] NOT NULL,
CONSTRAINT [PK_tblSiteClanGameDivision] PRIMARY KEY CLUSTERED
CREATE TABLE [dbo].[tblSiteGame](
[id] [int] IDENTITY(1,1) NOT NULL,
[name] [nvarchar](200) NOT NULL,
[description] [nvarchar](700) NULL,
[thumbnailLocation] [nvarchar](100) NULL,
[genreId] [int] NOT NULL,
[isDivision] [tinyint] NOT NULL,
CONSTRAINT [PK__tblGame__3213E83F03317E3D] PRIMARY KEY CLUSTERED
CREATE VIEW [dbo].[vwGetClanDivisions]
AS
SELECT dbo.tblSiteClanDetail.clanId, dbo.tblSiteClanDetail.clanName, dbo.tblSiteGame.id AS gameId, dbo.tblSiteGame.name AS gameName,
dbo.tblSiteClanGameDivision.removed, dbo.tblSiteClanGameDivision.dateAdded
FROM dbo.tblSiteClanDetail
INNER JOIN dbo.tblSiteClanGameDivision ON dbo.tblSiteClanDetail.id = dbo.tblSiteClanGameDivision.clanId
INNER JOIN dbo.tblSiteGame ON dbo.tblSiteClanGameDivision.gameId = dbo.tblSiteGame.id
GO
CREATE VIEW [dbo].[vwGetGameDivisions]
AS
SELECT id, name, thumbnailLocation, isDivision
FROM dbo.tblSiteGame
WHERE (isDivision = 1)
GO
The key is to use INNER JOIN to fetch the divisions the clan is signed up to, and LEFT JOIN to fetch the ones that they aren't. LEFT JOIN will return divisions even if the clan isn't signed up to them, and then you filter out the ones where they are, like so:
-- Get matching divisions
SELECT gd.ID, gd.name, cd.clanName
FROM dbo.vwGetClanDivisions cd
INNER JOIN dbo.vwGetGameDivisions gd ON cd.gameId = gd.id
-- Get remaining divisions
SELECT gd.ID, gd.name, cd.clanName
FROM dbo.vwGetClanDivisions cd
LEFT JOIN dbo.vwGetGameDivisions gd ON cd.gameId = gd.id
WHERE gd.gameName IS NULL
Notice that the second query uses a LEFT JOIN to get all rows on both sides of the join, and the WHERE clause only shows rows where there is no matching game division for that clan. You can also use table name aliases (as above) to shorten your queries.
Edit:
I should have been using a RIGHT OUTER JOIN, but in testing even that hasn't worked. However the method given by #Vincent James of using NOT IN does (with the caveat that you have to specify which clan you are searching by, otherwise you will only get a list of empty divisions). Here is a very basic SQL example:
CREATE TABLE #A (ID int, bID int)
CREATE TABLE #B (ID int)
INSERT INTO #B (ID) SELECT 1
INSERT INTO #B (ID) SELECT 2
INSERT INTO #B (ID) SELECT 3
INSERT INTO #B (ID) SELECT 4
INSERT INTO #B (ID) SELECT 5
INSERT INTO #A (ID, bID) SELECT 1,1
INSERT INTO #A (ID, bID) SELECT 1,2
INSERT INTO #A (ID, bID) SELECT 1,3
INSERT INTO #A (ID, bID) SELECT 2,1
INSERT INTO #A (ID, bID) SELECT 2,2
INSERT INTO #A (ID, bID) SELECT 3,3
INSERT INTO #A (ID, bID) SELECT 3,4
SELECT DISTINCT #B.ID
FROM #B
WHERE ID NOT IN(SELECT bID FROM #A WHERE ID = 1)
DROP TABLE #A
DROP TABLE #B
According to this, the entries for #A with an ID of 1 are linked to #B with an ID of 1, 2 or 3 (not 4 or 5). Running the above query shows that the NOT IN method returns 4 and 5, which is correct. I'm not entirely sure why the RIGHT OUTER JOIN isn't working, as the purpose of this is to return every row from #B regardless of if it was found in #A.
Edit 2:
Removed the Right Outer join method as I had two conflicting WHERE clauses that would prevent any rows being returned. It isn't possible to do it using an outer join with the given table structure, because you need to select by the ID on the left, but only where the left side is null. NOT IN is the correct method, as shown above.
Edit 3
Here is the stored procedure you should need to create. This is better than hard coding the query as it protects you from SQL injection attacks.
CREATE PROCEDURE [dbo].[GetEmptyClanDivisions]
(
#ClanID INT
)
AS
BEGIN
-- Get the clan name
DECLARE #ClanName nvarchar(255)
SET #ClanName = SELECT TOP 1 clanName FROM tblSiteClanDetail WHERE clanId = #ClanID
-- Get a list of divisions the clan is registered for
SELECT DISTINCT ID, name, #ClanName
FROM vwGetGameDivisions
WHERE ID NOT IN(
SELECT gameId
FROM tblSiteClanGameDivision
WHERE clanId = #ClanID
)
END
It is better to do the match with an ID field instead of a a name field.
SELECT dbo.vwGetGameDivisions.name
FROM dbo.vwGetGameDivisions
WHERE dbo.vwGetGameDivisions.name NOT IN (
SELECT dbo.vwGetGameDivisions.name
FROM dbo.vwGetClanDivisions
INNER JOIN dbo.vwGetGameDivisions ON dbo.vwGetClanDivisions.gameName = dbo.vwGetGameDivisions.name
)
I have a large table of user ids and another table of user records which contains a user post with user ids. The process is whenever a new feed post is retrieved,I do a request to the user id table for an id that is marked inactive ( I have that field ACTIVE because I have another process that creates these ids and inserts it continuously into table 1) and when an id is requested it is marked as inactive.
Then I check if the user exists in the user table(table 2) and if so return the user id associated with that user.
I was told that I can speed up this process but creating a hash table to do the lookup on table 2. I am not sure how to even start this and any links or samples will be appreciated.
Also I need to run a separate process that cleans table 1 and removes all inactive user ids.
When I call the procedure to insert into table 2, I pass the user id retrieved from table 1.
CREATE TABLE [dbo].[userforums]
(
[userid] [VARCHAR](16) NOT NULL CONSTRAINT [PK_forumssiteid] PRIMARY KEY CLUSTERED ,
[forumname] [VARCHAR](500) NOT NULL,
[exported] [INT] NULL,
[lastcrawled] [DATETIME] NULL,
[priority] [INT] NULL,
[origin] [VARCHAR](50) NULL,
[queryid] [VARCHAR](25) NULL,
[dateinserted] [DATETIME] NULL DEFAULT (getdate())
)
second table
CREATE TABLE [dbo].[userids]
(
[userid] [NVARCHAR](20) NOT NULL CONSTRAINT [PK_userids] PRIMARY KEY CLUSTERED,
[active] [NVARCHAR](20) NULL CONSTRAINT [IX_userids] UNIQUE NONCLUSTERED
)
get user id stored procedure
BEGIN TRANSACTION
SELECT TOP 1 #id = userid
FROM userids WITH (UPDLOCK, HOLDLOCK)
WHERE active = 'Y'
OR active IS NULL
UPDATE userids
SET active = 'N'
WHERE userid = #id
COMMIT TRANSACTION
check if userid exists
CREATE PROC Foo #forumname VARCHAR(500),
#userid VARCHAR(16),
#origin VARCHAR(50),
#queryid VARCHAR(25)
AS
SET NOCOUNT ON;
DECLARE #cnt INT
DECLARE #serverip VARCHAR(16)
DECLARE #mincnt INT
DECLARE #siteservercnt INT
SELECT #cnt = COUNT(*)
FROM userforums
WHERE forumname = #forumname
IF #cnt = 0
BEGIN
INSERT INTO userforums
(forumname,
userid,
exported,
origin,
queryid)
VALUES (#forumname,
#userid,
1,
#origin,
#queryid)
SELECT #siteservercnt = COUNT(*)
FROM siteserverip
WHERE userid = #userid
IF #siteservercnt = 0
BEGIN
SELECT TOP 1 #mincnt = COUNT(*),
#serverip = serverip
FROM siteserverip
GROUP BY serverip
ORDER BY COUNT(*)
SELECT TOP 1 #mincnt = sitecount,
#serverip = serverip
FROM serveripcounts
ORDER BY sitecount
INSERT INTO siteserverip
VALUES (#siteid,
#serverip)
UPDATE serveripcounts
SET sitecount = sitecount + 1
WHERE serverip = #serverip
END
END
SELECT userid
FROM userforums
WHERE forumname = #forumname
RETURN
Your existing dequeue query can be improved. Instead of
DECLARE #id INT
SELECT TOP 1 #id = userid
FROM userids WITH (UPDLOCK, HOLDLOCK)
WHERE active = 'Y'
OR active IS NULL
UPDATE userids
SET active = 'N'
WHERE userid = #id
Which is two operations (a clustered index scan followed by an index seek) you can do
UPDATE TOP (1) userids
WITH (ROWLOCK, READPAST)
SET active = 'N'
OUTPUT INSERTED.userid
WHERE active <> 'N'
Which is one operation and gives a plan with two range seeks.
A Hash table #TableName is a temporary object in tempdb that functions as a table. They are generally called 'temp tables'. I would NOT be using them as a first solution for retrieving data on the fly if this is a common occurrence. Instead I would create an index and see if that justifies your needs. Generally hash tables are used for intense operations where you want to get a set of things that may or may not be indexed and then relate it to something else and you want to keep it in memory.
I would create an index and that should improve speed. Also if you find is slow, a hash table won't speed that part up, it will just be putting a collection of that into a source to reuse seperated from the main table.
create index IX_[yourtableName]_[TableColumn(s)] on [Tablename]([Column(s)]
I would not create more objects unless necessary. Generally if your UserId's are valid ints you can search on them quite fast.
I am looking for the correct syntax and way to do the following directly from SQL: insert or update (if data already exists inside) TableMain from data contained in TableA with both having same composite primary key.
Both tables are defined as :
CREATE TABLE TableA (
[TID0] [int] NOT NULL,
[TID1] [int] NOT NULL,
[language] [nvarchar](2) NOT NULL,
[TID2] [nvarchar](200) NOT NULL,
[text] [nvarchar](max) NULL,
[updatedOn] [datetime] NOT NULL DEFAULT (getdate())
PRIMARY KEY (
[TID0],
[TID1],
[language],
[TID2],
)
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
TableA will be periodically deleted and filled.
TableMain as the same definition but will contain many more rows of data and what I need is to insert never seen values from TableA into TableMain, and update already existing rows.
I used to do this kind of insert but I do not know how to handle update and composite primary keys :
INSERT INTO TableMain
SELECT * FROM TableA
EDIT : i am using SQL Server 9.00.5000
EDIT : another way inspired by MERGE and mimick it
DECLARE #updatedIDs TABLE(
[TID0] [int],
[TID1] [int],
[language] [nvarchar](2),
[TID2] [nvarchar](200),
PRIMARY KEY ([TID0], [TID1], [language], [TID2]) -- as stated by Nikola Markovinović above, thanks
);
-- First update records
update TableMain
set [text] = source.[text],
[updatedOn] = source.[updatedOn]
OUTPUT
inserted.[TID0]
inserted.[TID1]
inserted.[language]
inserted.[TID2]
INTO #updatedIDs
from
TableMain AS main
, TableA AS source
WHERE
TableMain.[TID0] = source.[TID0]
and TableMain.[TID1] = source.[TID1]
and TableMain.[language] = source.[language]
and TableMain.[TID2] = source.[TID2]
-- And then insert
insert into TableMain
select *
from TableA AS source
where not exists
(
select 1
from #updatedIDs AS i
where i.[TID0] = source.[TID0]
and i.[TID1] = source.[TID1]
and i.[language] = source.[language]
and i.[TID2] = source.[TID2]
)
you should use a merge statment
something like this:
merge TableMain AS target
using TableA as source
ON <join tables here>
WHEN MATCHED THEN <update>
WHEN NOT MATCHED BY TARGET <Insert>
WHEN NOT MATCHED BY SOURCE <delete>
Here is a script you might use to upsert your data:
-- On error transaction is automatically rolled back
set xact_abort on
begin transaction
-- First update records
update TableMain
set [text] = source.[text],
[updatedOn] = source.[updatedOn]
from TableMain
inner join TableA source
on TableMain.[TID0] = source.[TID0]
and TableMain.[TID1] = source.[TID1]
and TableMain.[language] = source.[language]
and TableMain.[TID2] = source.[TID2]
-- And then insert
insert into TableMain ([TID0], [TID1], [language], [TID2], [text], [updatedOn])
select [TID0], [TID1], [language], [TID2], [text], [updatedOn]
from TableA source
where not exists
(
select *
from TableMain
where TableMain.[TID0] = source.[TID0]
and TableMain.[TID1] = source.[TID1]
and TableMain.[language] = source.[language]
and TableMain.[TID2] = source.[TID2]
)
commit transaction
You might rewrite not exists() as left join ... where TableMain.TID0 is null if performance is not satisfactory.
you can use the Merge command from SQLServer 2008. It allows you to merge data from another data source into your main data source and define specific behavior when there is a key match (and you'll probably like to update your table) or there is not match and you'll like to insert the new record.
http://blog.sqlauthority.com/2010/06/08/sql-server-merge-operations-insert-update-delete-in-single-execution/
you can visit this link to get few code samples.