SQL Server 2008: Select then update statement - sql

I'm doing a INNER JOIN across 6 tables for a module for my application, basically it takes the prostaff an gets their name and email, and compares that to a list of job applicants.
On the mod_employmentAppJobs I need to set a column for each row selected to true. Basically this sets a flag that tells the sql not to select the column because we already sent an email on that user. It's a bit field.
How do I set the emailSent field to true on a SQL statement? -- Coldfusion 8 is the Application server, just FYI....
SELECT *
FROM pro_Profile p
INNER JOIN pro_Email e ON p.profileID = e.profileID
INNER JOIN mod_userStatus m ON p.profileID = m.profileID
<!--- Joins the pro staff profiles to the employment app --->
INNER JOIN mod_employmentAppJobTitles a ON p.profileID = a.departmentID
<!--- Join Job titles to the jobs --->
INNER JOIN mod_employmentAppJobs b ON a.jobTitleID=b.jobTitleID
<!--- Joining the table on where the profile equals everything else --->
INNER JOIN mod_employmentAppProfile c ON c.eAppID = b.eAppID
WHERE b.emailSent = 'False'

You have a couple of choices.
1) Use a temp table and select data there first, update the mod_employmentAppJobs, and perform a select from the temp table to get your data retrieved.
So, it would look something like this.
create a temp table
CREATE TABLE #tmpTABLE
(
EmailAddress varchar(100),
JobTitle varchar(50),
JobTitleId bigint
......
)
Insert into it
INSERT INTO #tmpTable
SELECT EmailAddress,JobTitle, ........
FROM pro_Profile p
INNER JOIN pro_Email e
ON p.profileID = e.profileID
INNER JOIN mod_userStatus m
ON p.profileID = m.profileID
<!--- Joins the pro staff profiles to the employment app --->
INNER JOIN mod_employmentAppJobTitles a
ON p.profileID = a.departmentID
INNER JOIN mod_employmentAppJobs b
<!--- Join Job titles to the jobs --->
ON a.jobTitleID=b.jobTitleID
<!--- Joining the table on where the profile equals everything else --->
INNER JOIN mod_employmentAppProfile c
ON c.eAppID = b.eAppID
WHERE b.emailSent = 'False'
Update the source table (I'd recommend an index on jobTitleId in the temp table for performance if applicable)
UPDATE mod_employmentAddJobs
SET EmailSent="true"
FROM mod_employmentAppJobs b
INNER JOIN #tmpTable tmp
ON b.jobTitleID=tmp.jobTitleID
Get the actual data back to the app layer
SELECT * FROM #tmpTable
For better taste, I recommend sprinkling with BEGIN TRAN...COMMIT...ROLLBACK and BEGIN TRY..END TRY BEGIN CATCH...END CATCH to taste and to business requirements.
Also, it's good manners to drop the temp table after you are done with it, even though SQL server will not take offense if you don't.
2) You can use the OUTPUT clause of the update statement.
UPDATE mod_employmentAddJobs
SET EmailSent="true"
FROM pro_Profile p
INNER JOIN pro_Email e
ON p.profileID = e.profileID
INNER JOIN mod_userStatus m
ON p.profileID = m.profileID
<!--- Joins the pro staff profiles to the employment app --->
INNER JOIN mod_employmentAppJobTitles a
ON p.profileID = a.departmentID
INNER JOIN mod_employmentAppJobs b
<!--- Join Job titles to the jobs --->
ON a.jobTitleID=b.jobTitleID
<!--- Joining the table on where the profile equals everything else --->
INNER JOIN mod_employmentAppProfile c
ON c.eAppID = b.eAppID
WHERE b.emailSent = 'False'
OUTPUT inserted.*
This should get you the resultset right back to your app layer

You could store the result in a temporary table. You can use the data in the temporary table, and still return it at the end. It's a lot of typing but pretty straightforward:
-- Select data into temporary table
declare #result table (jobTitleID int, ...)
INSERT #result
(jobTitleID, ...)
SELECT jobTitleID
FROM pro_Profile p
...
-- Update unreadMail flag
update mod_employmentAppJobs
set unreadMail = 'False'
where jobTitleID in (select jobTitleId from #result)
-- Return result to application
select jobTitleId, ...
from #result

If you need to update and then return the data then probably the best is to use a Stored Procedure, where you first query the data, update it and then return it.

Well, this is just an idea, not the best solution.
You could get data (for which bit is false) in a DataReader and then execute a Command setting bit to true for ids contained in Dataset.
Dataset returns data you need...

I just tacked a on the end and it worked:
UPDATE mod_employmentAppJobs
SET emailSent = 'True'

Related

SQL Stored Procedure runs forever, but query alone ends normally

I'm trying to create a stored procedure on a SQL Server database used by our ERP, but I encounter a weird behavior.
When running the query within the procedure, I am able to fetch 10,000+ rows in about 5 seconds.
When I try to run the procedure, it runs forever while trying to create a table variable:
DECLARE #PTransco AS TABLE (IDProduit INT, IDFournisseur INT, ReferenceFournisseur VARCHAR(100), IDUniteAchat INT, CoefficientVentePartenaire NUMERIC(19,6) )
INSERT INTO #PTransco (IDProduit, IDFournisseur, ReferenceFournisseur, IDUniteAchat, CoefficientVentePartenaire)
SELECT P.ID, IFA.IDFournisseur, MAX(IFA.ReferenceFournisseur), MAX(IFA.IDUniteAchat), MAX(IFAU.CoefficientVentePartenaire)
FROM Product P
INNER JOIN Supplier IFA ON IFA.IDProduit = P.ID
INNER JOIN ThirdParty F ON F.ID = IFA.IDFournisseur
LEFT OUTER JOIN Supplier_U IFAU ON IFAU.ID = IFA.ID
WHERE F.CodeTiers IN (SELECT CodeTiers FROM #ListeTiersSociete) --Another local variable
GROUP BY P.ID, IDFournisseur
UNION
select P.ID, NULL, '999999', '280', 1 from TaxParam_Head PTE
inner join ThirdParty T on T.ID = PTE.IDSociete
inner join TaxParam_Product PTP on PTP.IDEntete = PTE.ID
inner join Product P on P.ID=PTP.IDProduit
where T.CodeTiers IN (SELECT CodeTiers FROM #ListeTiersSociete)
The procedure is declared as follows:
USE [ERP_Database]
GO
/****** Object: StoredProcedure [dbo].[PROC_Export_Data] Script Date: 09/09/2022 10:31:26 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[PROC_Export_Data]
#ReferenceDate AS DATE,
#SocietyCodeAS VARCHAR(MAX),
#Export BIT
AS
BEGIN
[Here are the queries]
Have you ever encountered that kind of behavior? Is there something wrong in my declaration?
I first envisioned that my queries were wrong, so I ran them separately, but the results were almost instantaneous.
Running all my queries out of the procedure work as expected (results are fetched).
--
EDIT : Thanks to #Stu, I realized the error is on the following query :
DECLARE #BonsDateExport AS TABLE (IDBon INT, CodeSociete VARCHAR(50), DateBon DATE, DateExport DATE, DateFiltre DATE)
INSERT INTO #BonsDateExport (IDBon, CodeSociete, DateBon)
SELECT EF.ID, SOC.CodeTiers, EF.DateOperation
FROM InvoiceLine LF
inner join InvoiceHeader EF on EF.ID=LF.IDFacture
INNER JOIN OrderHeader EB on LF.NumeroBon = EB.NumeroBon
INNER JOIN UTIL_FILTER_PartnerOrder F ON F.ID = EB.ID
INNER JOIN ThirdParty SOC ON SOC.ID = EB.IDSociete
INNER JOIN DocumentType TD ON TD.ID = EB.IDTypeDocument
LEFT OUTER JOIN OrderHeader_U EBU ON EBU.ID = EB.ID
WHERE SOC.CodeTiers IN (SELECT CodeTiers FROM #ListeTiersSociete)
AND TD.CodeTypeDocument IN ('BLF', 'BLD')
AND EBU.IDEventExportDesBLPartenaires IS NULL
The part that hangs is the insert. Note that the SELECT fetches 1000+ lines in less than a second when ran on its own. The other fields are updated later depending on the previously inserted values.
EDIT 2 :
I managed to make it work. The hanging came from the query on the local variable #ListeTiersSociete created in the beginning of the procedure and containing only 1 row.
From what I understood from this other question, it may or may not have something to do with parameter sniffing. I checked with the end users, and I can safely remove this particular part.
Thanks a lot for your time. I'll add this as an answer right away.
I would suggest to replace Table variable with Local Temp Table,
CREATE TABLE #BonsDateExport (IDBon INT, CodeSociete VARCHAR(50), DateBon DATE, DateExport DATE, DateFiltre DATE)
INSERT INTO #BonsDateExport (IDBon, CodeSociete, DateBon)
SELECT EF.ID, SOC.CodeTiers, EF.DateOperation
FROM InvoiceLine LF
inner join InvoiceHeader EF on EF.ID=LF.IDFacture
INNER JOIN OrderHeader EB on LF.NumeroBon = EB.NumeroBon
INNER JOIN UTIL_FILTER_PartnerOrder F ON F.ID = EB.ID
INNER JOIN ThirdParty SOC ON SOC.ID = EB.IDSociete
INNER JOIN DocumentType TD ON TD.ID = EB.IDTypeDocument
LEFT OUTER JOIN OrderHeader_U EBU ON EBU.ID = EB.ID
WHERE SOC.CodeTiers IN (SELECT CodeTiers FROM #ListeTiersSociete)
AND TD.CodeTypeDocument IN ('BLF', 'BLD')
AND EBU.IDEventExportDesBLPartenaires IS NULL
The issue seemed to come from this particular line:
WHERE SOC.CodeTiers IN (SELECT CodeTiers FROM #ListeTiersSociete)
Or more accurately, from the definition of this local variable (even though the resulting table only contained 1 row) :
DECLARE #ListeTiersSociete AS TABLE (ID INT, CodeTiers VARCHAR(100))
INSERT INTO #ListeTiersSociete (ID, CodeTiers)
SELECT SOC.ID, SOC.CodeTiers
FROM ThirdParty SOC
INNER JOIN dbo.STR_Split(#LocalListeSoc, '|') LCS ON LCS.Value = SOC.CodeTiers
WHERE SOC.CodeTiers IN ('SDL')--('NVL', 'SDL', 'LAC')
(The procedure is supposed to be expanded to take into account more societies in the near future)
Removing this variable and using the input variable directly after restraining it to a single value in the ERP made the procedure run smoothly.
Thanks a lot for your help.

Update with join and temp table

I have table that called hanpaka there I want to update the cardNum from temp table the connection between the two is IDMember and MemberId. but updating like this cause multiple cardNum not to the right MemberId why?
UPDATE Knowledge4All..Hanpaka
SET CardNum = (c.CardNumber )
FROM #Temp2 c inner join Hanpaka h on IDMember = h.MemberId`
Try using the alias in the update:
UPDATE h
SET CardNum = c.CardNumber
FROM #Temp2 c JOIN
Hanpaka h
ON c.IDMember = h.MemberId;
SQL Server does allow the table to be repeated in the update. However, it might get confused and your query might be doing a Cartesian product.

string_agg is to slow with big data and i need a faster solution

I am working with contacting a string from a result and it is taking to long to execute
this is the solution I have:
declare #damagedParties table (caseid varchar(max),FirstName varchar(max),LastName varchar(max),damagedName varchar(max))
insert #damagedParties
select t.caseid,ped.FirstName as FirstName,ped.LastName,concat(ped.FirstName,' ',ped.LastName) as damagedName
from [Case] t
inner join [KCC].[dbo].[Party] p1 on p1.CaseId = t.caseid
LEFT JOIN Person ped ON ped.PersonOrBusinessId = ped.PersonOrBusinessId and p1.PartyTypeRefData = 'kpcparty$PARTY_INJUREDPARTY_F'
select string_agg(d.damagedName,', ')
from #damagedParties d
group by d.caseid
The LEFT JOIN condition in the first query joins the Person table to itself on PersonOrBusinessId. Presumably, the JOIN should be from Person table to the Party table? Something like this
insert #damagedParties
select t.caseid, ped.FirstName, ped.LastName,
concat(ped.FirstName,' ',ped.LastName) as damagedName
from [Case] t
join [KCC].[dbo].[Party] p1 on p1.CaseId = t.caseid
left join Person ped ON p1.PersonOrBusinessId = ped.PersonOrBusinessId
where p1.PartyTypeRefData = 'kpcparty$PARTY_INJUREDPARTY_F';

How to insert results of select

I am a C# dev learning SQL.
I need to publish data from one database, to another which already has the same structure and which are both on the same instance of Sql Server. By publish, I mean I want all the id's and data to be exactly the same.
The second database is an exact copy of the first.
This is the query:
Select vev.* from [dbo].[metadata_document_set] mds
inner join
[dbo].[document_metadata] dm
on mds.metadata_document_set_id = dm.metadata_document_set_id
inner join [dbo].[segment_metadata] sm
on dm.document_metadata_id = sm.document_metadata_id
inner join [dbo].[element_metadata] em
on sm.segment_metadata_id = em.segment_metadata_id
inner join [dbo].[valid_element_value] vev
on em.element_metadata_id = vev.element_metadata_id
where mds.code = 'PA'
I need to insert all the rows of valid_element_values, as well as element_metadata, segment_metadata, and document_metadata.
How do I do that?
If you are inserting to separate tables, you will have to do it one insert at a time.
insert [new_database_name].[dbo].[metadata_document_set]
Select * from [old_database_name].[dbo].[metadata_document_set]
where code = 'PA'
insert [new_database_name].[dbo].[document_metadata]
Select * from [old_database_name].[dbo].[document_metadata]
<filter as required>
and so on.
This assumes your tables are exactly the same.
NB This will not work if you have INDENTITY columns.
Select vev.* will select columns only from valid_element_value table so remove vev.* from select and mention the columns from all four table in same order as your Insert column list
Considering the target tables are created with all the required column in target database
Update : Looks like you want to insert into four different tables. Then you need four different Insert statements
INSERT INTO target_database.SCHEMA.target_valid_element_value
(vev_col1,
vev_col2,
..)
SELECT vev.col1,
vev.col2,
..
FROM [dbo].[metadata_document_set] mds
INNER JOIN [dbo].[document_metadata] dm
ON mds.metadata_document_set_id = dm.metadata_document_set_id
INNER JOIN [dbo].[segment_metadata] sm
ON dm.document_metadata_id = sm.document_metadata_id
INNER JOIN [dbo].[element_metadata] em
ON sm.segment_metadata_id = em.segment_metadata_id
INNER JOIN [dbo].[valid_element_value] vev
ON em.element_metadata_id = vev.element_metadata_id
WHERE mds.code = 'PA'
INSERT INTO target_database.SCHEMA.target_element_metadata
(em_col1,
em_col2,
..)
SELECT em.col1,
em.col2,
..
FROM [dbo].[metadata_document_set] mds
INNER JOIN [dbo].[document_metadata] dm
ON mds.metadata_document_set_id = dm.metadata_document_set_id
INNER JOIN [dbo].[segment_metadata] sm
ON dm.document_metadata_id = sm.document_metadata_id
INNER JOIN [dbo].[element_metadata] em
ON sm.segment_metadata_id = em.segment_metadata_id
INNER JOIN [dbo].[valid_element_value] vev
ON em.element_metadata_id = vev.element_metadata_id
WHERE mds.code = 'PA'
Regarding Identity, If you have identity column in your target tables and you want insert values from source for those columns as well then
set Identity_insert target_table ON
<<Insert statement>>
set Identity_insert target_table OFF
If you dont want to explicitly insert values into identity column then dont add identity column in Insert & Select column list
Lets say you have [dbo1].[metadata_document_set1] table (and it has all the columns in select query)then
INSERT INTO [dbo1].[metadata_document_set1]
Select vev.*,em.*,sm.*,dm.*,mds.* from [dbo].[metadata_document_set] mds
inner join [dbo].[document_metadata] dm
on mds.metadata_document_set_id = dm.metadata_document_set_id
inner join [dbo].[segment_metadata] sm
on dm.document_metadata_id = sm.document_metadata_id
inner join [dbo].[element_metadata] em
on sm.segment_metadata_id = em.segment_metadata_id
inner join [dbo].[valid_element_value] vev
on em.element_metadata_id = vev.element_metadata_id
where mds.code = 'PA'

Receiving SQL Object Already named 'x' in the DB, Even after dropping

IF OBJECT_ID('#TempTableHoldingRepairsList') IS NOT NULL
DROP TABLE #TempTableHoldingRepairsList
GO
SELECT
a.EnteredDate,
bb.EmployeeId,
bb.EmployeeName,
dd.EquipmentId,
dd.EquipmentName,
ee.PK_WorkOrder,
StatusName = 'NOT-SERVICEABLE'
INTO #TempTableHoldingRepairsList
FROM dbo.PIT_Inspection a
INNER JOIN dbo.PIT_EmployeeName bb
ON a.FK_EmployeeName = bb.PK_EmployeeName
INNER JOIN dbo.PIT_EquipmentName dd
ON a.FK_EquipmentName = dd.PK_EquipmentName
LEFT JOIN dbo.PIT_WorkOrder ee
ON ee.FK_EmployeeName = bb.PK_EmployeeName
WHERE a.FK_Status = 2
GROUP BY ee.PK_WorkOrder, a.EnteredDate, bb.EmployeeId, bb.EmployeeName, dd.EquipmentId, dd.EquipmentName
GO
--Now Count how many work orders for each.
SELECT COUNT(*) AS WorkOrderCount FROM PIT_WorkOrder pw
INNER JOIN #TempTableHoldingRepairsList th
ON th.PK_WorkOrder = pw.PK_WorkOrder
Not sure if I'm doing the final inner join wrong or missing something completely, but I continue to receive:
There is already an object named '#TempTableHoldingRepairsList' in the database.
You can't check for the existence of a temporary table directly they way you're doing it. You need to look in tempdb. You need to change the check to the following:
IF OBJECT_ID('tempdb..#TempTableHoldingRepairsList') IS NOT NULL
DROP TABLE #TempTableHoldingRepairsList
GO