I wish to delete a result a select statement returns, reason I'm doing this is because I have relationships between tables and if I delete from the top-most table its children rows in other tables have to be deleted, too.
Can anyone correct this stored procedure for me please?
ALTER proc [dbo].[storedprocname]
(#Parameter uniqueidentifier = '00000000-0000-0000-0000-000000000000')
AS BEGIN
DELETE FROM TableOne
WHERE IDOne IN
(SELECT
IDOne,
DescOne, IndexOne,
IDTwo,
QuestionTwo, ControlTypeTwo, IndexTwo,
IDThree,
DescThree, IndexThree,
QuestionFour,
OptionFour
FROM
TableOne
INNER JOIN
TableTwo ON TableTwo.CatID = TableOne.IDOne
INNER JOIN
TableThree ON TableThree.Question = TableTwo.IDTwo
LEFT OUTER JOIN
TableFour ON TableFour.Question = TableThree.IDThree
WHERE
TableOne.IDOne = #Parameter)
END
ALTER proc [dbo].[storedprocname]
(#Parameter uniqueidentifier = '00000000-0000-0000-0000-000000000000')
AS BEGIN
DELETE FROM TableOne
WHERE IDOne IN
(SELECT
IDOne
FROM
TableOne
INNER JOIN
TableTwo ON TableTwo.CatID = TableOne.IDOne
INNER JOIN
TableThree ON TableThree.Question = TableTwo.IDTwo
LEFT OUTER JOIN
TableFour ON TableFour.Question = TableThree.IDThree
WHERE
TableOne.IDOne = #Parameter)
END
Since you want to delete all rows where IDOne is in a list of possible values - then you need to make sure the subquery after the IN (...) also returns a single column which can be used to compare! After all, you cannot compare a single IDOne value to the whole list of columns that you're currently returning ....
Try something like this:
ALTER proc [dbo].[storedprocname]
(#Parameter uniqueidentifier = '00000000-0000-0000-0000-000000000000')
AS BEGIN
DELETE FROM TableOne
WHERE IDOne IN
(SELECT
IDOne
FROM
TableOne
INNER JOIN
TableTwo ON TableTwo.CatID = TableOne.IDOne
INNER JOIN
TableThree ON TableThree.Question = TableTwo.IDTwo
LEFT OUTER JOIN
TableFour ON TableFour.Question = TableThree.IDThree
WHERE
TableOne.IDOne = #Parameter)
END
I don't know (your question is too vague and not clear enough) whether all those JOIN's inside the subquery are really needed .... you might need to tweak that to your requirements.
Related
What would the syntax be to convert this MS Access query to run in SQL Server as it doesn't have a DistinctRow keyword
UPDATE DISTINCTROW [MyTable]
INNER JOIN [AnotherTable] ON ([MyTable].J5BINB = [AnotherTable].GKBINB)
AND ([MyTable].J5BHNB = [AnotherTable].GKBHNB)
AND ([MyTable].J5BDCD = [AnotherTable].GKBDCD)
SET [AnotherTable].TessereCorso = [MyTable].[J5F7NR];
DISTINCTROW [MyTable] removes duplicate MyTable entries from the results. Example:
select distinctrow items
items.item_number, items.name
from items
join orders on orders.item_id = items.id;
In spite of the join getting you the same item_number and name multiple times when there is more than one order for it, DISTINCTROW reduces this to one row per item. So the whole join is merely for assuring that you only select items for which exist at least one order. You don't find DISTINCTROW in any other DBMS as far as I know. Probably because it is not needed. When checking for existence, we use EXISTS of course (or IN for that matter).
You are joining MyTable and AnotherTable and expect for some reason to get the same MyTable record multifold for one AnotherTable record, so you use DISTINCTROW to only get it once. Your query would (hopefully) fail if you got two different MyTable records for one AnotherTable record.
What the update does is:
update anothertable
set tesserecorso = (select top 1 j5f7nr from mytable where mytable.j5binb = anothertable.gkbinb and ...)
where exists (select * from mytable where mytable.j5binb = anothertable.gkbinb and ...)
But this uses about the same subquery twice. So we'd want to update from a query instead.
The easiest way to get one result record per <some columns> in a standard SQL query is to aggregate data:
select *
from anothertable a
join
(
select j5binb, j5bhnb, j5bdcd, max(j5f7nr) as j5f7nr
from mytable
group by j5binb, j5bhnb, j5bdcd
) m on m.j5binb = a.gkbinb and m.j5bhnb = a.gkbhnb and m.j5bdcd = a.gkbdcd;
How to write an updateble query is different from one DBMS to another. Here is the final update statement for SQL-Server:
update a
set a.tesserecorso = m.j5f7nr
from anothertable a
join
(
select j5binb, j5bhnb, j5bdcd, max(j5f7nr) as j5f7nr
from mytable
group by j5binb, j5bhnb, j5bdcd
) m on m.j5binb = a.gkbinb and m.j5bhnb = a.gkbhnb and m.j5bdcd = a.gkbdcd;
The DISTINCTROW predicate in MS Access SQL removes duplicates across all fields of a table in join statements and not just the selected fields of query (which DISTINCT in practically all SQL dialects do). So consider selecting all fields in a derived table with DISTINCT predicate:
UPDATE [AnotherTable]
SET [AnotherTable].TessereCorso = main.[J5F7NR]
FROM
(SELECT DISTINCT m.* FROM [MyTable] m) As main
INNER JOIN [AnotherTable]
ON (main.J5BINB = [AnotherTable].GKBINB)
AND (main.J5BHNB = [AnotherTable].GKBHNB)
AND (main.J5BDCD = [AnotherTable].GKBDCD)
Another variant of the query.. (Too lazy to get the original tables).
But like the query above updates 35 rows =, so does this one
UPDATE [Albi-Anagrafe-Associati]
SET
[Albi-Anagrafe-Associati].CRegDitte = [055- Registri ditte].[CRegDitte],
[Albi-Anagrafe-Associati].NIscrTribunale = [055- Registri ditte].[NIscrTribunale],
[Albi-Anagrafe-Associati].NRegImprese = [055- Registri ditte].[NRegImprese]
FROM [055- Registri ditte]
WHERE EXISTS(
SELECT *
FROM [055- Registri ditte]-- [Albi-Anagrafe-Associati]
WHERE ([055- Registri ditte].GIBINB = [Albi-Anagrafe-Associati].GKBINB)
AND ([055- Registri ditte].GIBHNB = [Albi-Anagrafe-Associati].GKBHNB)
AND ([055- Registri ditte].GIBDCD = [Albi-Anagrafe-Associati].GKBDCD))
Update [AnotherTable]
Set [AnotherTable].TessereCorso = MyTable.[J5F7NR]
From [AnotherTable]
Inner Join
(
Select Distinct [J5BINB],[5BHNB],[J5BDCD]
,(Select Top 1 [J5F7NR] From MyTable) as [J5F7NR]
,[J5BHNB]
From MyTable
)as MyTable
On (MyTable.J5BINB = [AnotherTable].GKBINB)
AND (MyTable.J5BHNB = [AnotherTable].GKBHNB)
AND (MyTable.J5BDCD = [AnotherTable].GKBDCD)
The three tables in question are:
Table A - relevant columns are TimeTicket and IdAddress
Table B - relevant columns are CommunicationNumber, TimeCreate and IdAddress.
Table C - relevant columns are CommunicationNumber, LastCalled, NextCall
Table C is created by a join of TableA and TableB on IdAddress
INSERT INTO tblC ([CommunicationNumber], [LastCalled] ,[NextCall])
SELECT T2.CommunicationNumber, T2.TimeCreate, T1.TimeTicket
FROM tblA T1
INNER JOIN tblB T2
ON T1.IdAddress = T2.IdAddress AND T2.CommunicationNumber IS NOT NULL
That's one part of the process, and that's fine.
Now, when there is new data in Table A and Table B, I want to update the data entries in Table C. However, I want to ignore the values from Table A and Table B that I have already entered into Table C.
To achieve this, I used NOT EXISTS and wrote a query that looks like this.
INSERT INTO tblC ([CommunicationNumber], [LastCalled] ,[NextCall])
SELECT T2.CommunicationNumber, T2.TimeCreate, T1.TimeTicket
FROM tblA T1
INNER JOIN tblB T2
ON T1.IdAddress = T2.IdAddress AND T2.CommunicationNumber IS NOT NULL
WHERE NOT EXISTS (SELECT T3.CommunicationNumber
FROM [dbo].[tblPhoneLogRep] T3
WHERE T1.TimeTicket <> T3.NextCall AND T2.TimeCreate <> T3.LastCalled AND T2.CommunicationNumber <> T3.CommunicationNumber)
However, this query always returns an empty set.
Could someone please explain to me what is it that I am doing incorrectly?
Try using the EXCEPT set operator:
INSERT INTO tblC ([CommunicationNumber], [LastCalled] ,[NextCall])
SELECT T2.CommunicationNumber, T2.TimeCreate, T1.TimeTicket
FROM tblA T1
INNER JOIN tblB T2
ON T1.IdAddress = T2.IdAddress AND T2.CommunicationNumber IS NOT NULL
EXCEPT
SELECT CommunicationNumber, LastCalled, NextCall FROM tblC
To fix your existing query, you would need to change your <> operators to = operators, like so:
INSERT INTO tblC ([CommunicationNumber], [LastCalled] ,[NextCall])
SELECT T2.CommunicationNumber, T2.TimeCreate, T1.TimeTicket
FROM tblA T1
INNER JOIN tblB T2
ON T1.IdAddress = T2.IdAddress AND T2.CommunicationNumber IS NOT NULL
WHERE NOT EXISTS (SELECT 1
FROM tblC
WHERE T1.TimeTicket = tblC.NextCall AND T2.TimeCreate = tblC.LastCalled AND T2.CommunicationNumber = tblC.CommunicationNumber)
Personally, I think the EXCEPT syntax is more clear though.
Your issue is that you are essentially using a double negative. You are saying NOT EXISTS and you are setting your WHERE criteria to <>. I think it would work out if you either used EXISTS or change you criteria =.
Is it possible to do the following:
IF [a] = 1234 THEN JOIN ON TableA
ELSE JOIN ON TableB
If so, what is the correct syntax?
I think what you are asking for will work by joining the Initial table to both Option_A and Option_B using LEFT JOIN, which will produce something like this:
Initial LEFT JOIN Option_A LEFT JOIN NULL
OR
Initial LEFT JOIN NULL LEFT JOIN Option_B
Example code:
SELECT i.*, COALESCE(a.id, b.id) as Option_Id, COALESCE(a.name, b.name) as Option_Name
FROM Initial_Table i
LEFT JOIN Option_A_Table a ON a.initial_id = i.id AND i.special_value = 1234
LEFT JOIN Option_B_Table b ON b.initial_id = i.id AND i.special_value <> 1234
Once you have done this, you 'ignore' the set of NULLS. The additional trick here is in the SELECT line, where you need to decide what to do with the NULL fields. If the Option_A and Option_B tables are similar, then you can use the COALESCE function to return the first NON NULL value (as per the example).
The other option is that you will simply have to list the Option_A fields and the Option_B fields, and let whatever is using the ResultSet to handle determining which fields to use.
This is just to add the point that query can be constructed dynamically based on conditions.
An example is given below.
DECLARE #a INT = 1235
DECLARE #sql VARCHAR(MAX) = 'SELECT * FROM [sourceTable] S JOIN ' + IIF(#a = 1234,'[TableA] A ON A.col = S.col','[TableB] B ON B.col = S.col')
EXEC(#sql)
--Query will be
/*
SELECT * FROM [sourceTable] S JOIN [TableB] B ON B.col = S.col
*/
You can solve this with union
select a, b
from tablea
join tableb on tablea.a = tableb.a
where b = 1234
union
select a, b
from tablea
join tablec on tablec.a = tableb.a
where b <> 1234
I disagree with the solution suggesting 2 left joins. I think a table-valued function is more appropriate so you don't have all the coalescing and additional joins for each condition you would have.
CREATE FUNCTION f_GetData (
#Logic VARCHAR(50)
) RETURNS #Results TABLE (
Content VARCHAR(100)
) AS
BEGIN
IF #Logic = '1234'
INSERT #Results
SELECT Content
FROM Table_1
ELSE
INSERT #Results
SELECT Content
FROM Table_2
RETURN
END
GO
SELECT *
FROM InputTable
CROSS APPLY f_GetData(InputTable.Logic) T
I think it will be better to think about your query in a different way and treat them more like sets.
I do believe if you make two separate queries then join them using UNION, It will be much better in performance and more readable.
How do I make a T-SQL stored procedure that returns the (tabular) result of this SELECT statement:
USE automation
SELECT insurer.name,
insurer.case_name,
contact.name,
contact_address.line_1,
contact_address.city,
state.state_abbr,
contact_address.zip
FROM person as insurer
INNER JOIN persons_relationship on persons_relationship.person2_id = insurer.person_id
INNER JOIN person contact on contact.person_id = persons_relationship.person_id
INNER JOIN person_address contact_person_address on contact_person_address.person_id = contact.person_id
INNER JOIN address contact_address on contact_address.address_id = contact_person_address.address_id
INNER JOIN state on state.state_id = contact_address.state_id
insurer.person_class_id = 2
Do something like this:
USE Automation
GO
CREATE PROCEDURE dbo.YourProcedureNameHere
AS
SELECT
insurer.name,
insurer.case_name,
contact.name,
contact_address.line_1,
contact_address.city,
state.state_abbr,
contact_address.zip
FROM
person as insurer
INNER JOIN
persons_relationship on persons_relationship.person2_id = insurer.person_id
INNER JOIN
person contact on contact.person_id = persons_relationship.person_id
INNER JOIN
person_address contact_person_address on contact_person_address.person_id = contact.person_id
INNER JOIN
address contact_address on contact_address.address_id = contact_person_address.address_id
INNER JOIN
state on state.state_id = contact_address.state_id
and you're done. Now you can call your statement as:
EXEC sp_executesql N'dbo.YourProcedureNameHere'
I suggest you also put
SET NOCOUNT ON
Straight after the AS. Some data access libraries are confused by the x records affected message and this turns it off.
put this query in the stored procedure and when you will run this stored procedure you will get tabular result
I would like to decide Inner or outer join ( to the same table) depending on the value.
How do I do this?
Thanks
I have all my select values here
Then bunch of joins
And on the last join I would like to do
CASE
WHEN RTRIM(LTRIM(#AccountType)) = 'OFX' THEN
INNER JOIN Subscriber.Access.SubscriberOFXAccount ASOA
ON SSA.Id = ASOA.SubscriberOFXAccountId AND ASOA.Active = 1
ELSE
LEFT JOIN Subscriber.Access.SubscriberOFXAccount ASOA2
ON SSA.Id = ASOA2.SubscriberOFXAccountId
You just use an if/else condition to make up the SQL Statement completly:
IF RTRIM(LTRIM(#AccountType)) = 'OFX' THEN
BEGIN
SELECT Blah FROM Table WHERE Blah2=SomeThings INNER JOIN MyOtherFooBar ON ...
END
ELSE
BEGIN
SELECT Blah FROM Table WHERE Blah2=SomeThings LEFT JOIN MyOtherFooBar ON ...
END
How about:
Select ..
From ...
Left Join Subscriber.Access.SubscriberOFXAccount ASOA
ON SSA.Id = ASOA.SubscriberOFXAccountId
Where RTRIM(LTRIM(#AccountType)) <> 'OFX' Or ASOA.IsActive = 1
Note that by check for IsActive = 1 on a left join, it effectively means a row must exist and thus when #AccountType is not OFX, it will effectively use an inner join.