TSQL - What Did I Update Here? - sql-server

update outbound.notification
set statuscode = 1
select * from outbound.Notification
where statuscode = 4 --not in (3,6)
and convert(varchar,createdtimestamp,102) > '2015.01.10'
and OutboundID <> 647
Executed the above query. Meant to have the select commented out, so that I could alternate between selecting queries in that status and updating them. What did I do to what rows?

You updated all rows of outbound.Notification. Your query is generally two separate queries.
First, this updates all rows of outbound.Notification:
update outbound.notification set statuscode = 1
Second, this is just a SELECT statement.
select * from outbound.Notification
where statuscode = 4 --not in (3,6)
and convert(varchar,createdtimestamp,102) > '2015.01.10'
and OutboundID <> 647

Related

SQL UPDATE statement does not update all rows in one execution

I have a table in MS SQL Server where the EXPECTED result should look like this:
Prior to expected result/query execution, all FieldX values are NULL. When I run my query, FieldX is only updated from row 2 to 8.
I need to UPDATE FieldX using a set of rules, which I define as such:
WITH cte_previous_rows AS (
SELECT Date, Staff_Id, LAG(FieldX) OVER (partition by Staff_Id ORDER by [date]) as Prev_Row
FROM Sales
) UPDATE Sales
SET FieldX = (CASE
WHEN Staff_id_sales < 1500 AND ClosedSale = 0 THEN 0
WHEN Staff_id_sales = 1500 and ClosedSale = 0 THEN 5
WHEN Staff_id_sales <= 3000 and Staff_id_sales > 1500 and ClosedSale = 0 THEN 1
WHEN Staff_id_sales > 3000 and (c.Prev_Row = 1 OR c.Prev_Row = 0) THEN 2
WHEN Staff_id_sales > 3000 and (c.Prev_Row = 2 or c.Prev_Row = 3) THEN 3
ELSE FieldX
END)
FROM Sales
JOIN cte_previous_rows as c ON Sales.staff_id = c.staff_id AND Sales.Date = c.Date;
This query works just fine. But the problem lies in the last two WHEN statements. The reason for this, is of course that c.Prev_Row (previous row) is used in the rule set for these two last WHEN statements..
How can I edit my query so that the above rule set is applied on to all 50k rows in a SINGLE execution? Perhaps a new method is required..
A recursive CTE that works from the earliest row for each Staff_Id forward may be the ticket:
Note: This query was not run on an image of the data, so it might have some errors.

Same query, with or without condition.

I want to write a query that selects rows by condition, but if there is no response for that condition, the code should select the same columns, but without the condition.
What the right way to do this? Thanks!
This is my example - select this:
select top 1 *
from tbl
where isActive = 1
but if there is no response, select this instead:
select top 1 *
from tbl
Note that the query is big and complex, so I prefer not to select one and then select the second one, if the first one is null. Also because I have a union after this and it throws an error with this syntax.
Assuming your query is a select top x ... query I would simply use the order by as suggested by Peter's answer. Assuming it's not involving top x, since you wrote your actual query is big and complicated, you can use a common table expression.
The idea is that you encapsulate the big and complex query inside the cte, but instead of writing the where clause to filter out records, you use a case expression to return 1 or 0 if the condition is true or false for each record.
Then you select from that cte where either the case expression results with 1 or there are no records in the cte where the case expression results with 1.
Create and populate sample table (Please save us this step in your future questions)
DECLARE #T AS TABLE
(
Id int identity(1,1),
IsActive bit
)
INSERT INTO #T VALUES
(1),(1),(NULL),(1),(1),(NULL),
(1),(1),(NULL),(1),(1),(NULL),
(1),(1),(NULL),(1),(1),(NULL),
(1),(1),(NULL),(1),(1),(NULL)
The common table expression:
;WITH CTE AS
(
SELECT Id, IsActive,
CASE WHEN IsActive = 1 THEN 1
ELSE 0
END As FoundRecords
FROM #T
)
The query:
SELECT Id, IsActive
FROM CTE
WHERE FoundRecords = 1
OR NOT EXISTS
(
SELECT 1
FROM CTE
WHERE FoundRecords = 1
)
Results:
Id IsActive
1 True
2 True
4 True
5 True
7 True
8 True
10 True
11 True
13 True
14 True
16 True
17 True
19 True
20 True
22 True
23 True
You can see a live demo on rextester
The simplest way is to not use where isActive = 1 but instead order by isActive in descending order:
select top 1 *
from tbl
order by isActive desc
You might even want to consider adding additional ordering fields (e.g. id, name, date), because without that the resulting item could be unpredictable.
You can use the IF-Else condition, right?
DECLARE #result INT
SET #result = CONVERT(INT, (SELECT COUNT(*) FROM tbl WHERE isActive = 1))
IF (#result > 0)
BEGIN
select top 1 * from tbl where isActive = 1;
END
ELSE
BEGIN
select top 1 * from tbl;
END

Trying to Populate a Column with a Query

So I'm doing a data mining project for one of my classes. As part of it, I'm trying to apply Min Max Normalization to some of the data- which is the easy part. The hard part had been actually inserting the results of the queries into the table.
At first, I tried an INSERT INTO statement...
insert into dbo.CountsA([TotalCountMinMAx])
SELECT
1.00*(TotalCount-MinCount)/CountRange as TotalCountMinMax
FROM
(
SELECT
TotalCount,
MIN(TotalCount) OVER () AS MinCount,
MAX(TotalCount) OVER () - MIN(TotalCount) OVER () AS CountRange
FROM
dbo.CountsA
) X
The subquery itself works fine, but the moment I tried inserting the results into the table, it only inserted a number of null records. So instead of, say, updating ten entries in the TotalCountMinMAx column, it created ten additional records, and set all the columns to NULL.
After busting my head trying to figure that out, I tried using an UPDATE query instead.
update dbo.CountsA
set [TotalCountMinMAx]=(
SELECT
1.00*(TotalCount-MinCount)/CountRange as TotalCountMinMax
FROM
(
SELECT
TotalCount,
MIN(TotalCount) OVER () AS MinCount,
MAX(TotalCount) OVER () - MIN(TotalCount) OVER () AS CountRange
FROM
dbo.CountsA
) X)
This query failed to run entirely.
"Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression."
At this point, short of digging out my old SQL book and basically relearning SQL from scratch (I am very, very rusty), I'm out of ideas for making either of these codes work.
In case 1, when you insert, data will add row below old data. Example, if your table like:
ID | Col
1 2
2 4
After you insert just only Col column values: 3,4
Your table like this:
ID | Col
1 2
2 4
NULL 3
NULL 4
In case 2:
IF you use sub-query to insert like:
UPDATE Your_Table
SET Col = (<sub-query>)
sub-query must return a single value.
You can add where clause make sub-query return a single value, like this:
UPDATE Your_Table
SET Col = (SELECT ... FROM Your_Table) AS A
WHERE Col_ID = A.Col_ID
The problem is you are not correlating your sub-query that's the reason to get Sub-query retuned more than one row error.
Try using CTE to update which is easy and more readable.
;WITH cte
AS (SELECT 1.00 * ( TotalCount - MinCount ) / CountRange AS TotalCountMinMax_N,
TotalCountMinMax
FROM (SELECT TotalCount,
TotalCountMinMax,
Min(TotalCount)
OVER () AS MinCount,
Max(TotalCount)
OVER () - Min(TotalCount)
OVER () AS CountRange
FROM dbo.CountsA) X)
UPDATE cte
SET TotalCountMinMax = TotalCountMinMax_N

UPDATE FROM runs on entire table

I have tried to run an update on a list of records and modify a column based on a few calculations. This is my query (my query actually does a redistribution of amounts into NET and VAT):
update transaction_details
set amount = (
CASE WHEN id = x.netID THEN x.calculated_NET
WHEN id = x.vatID THEN x.calculated_VAT
END
)
from
(select
d1.id as 'netHmy',
d2.id as 'vatHmy',
round(((d1.amount + d2.amount )/1.18) * 0.18, 2) 'calculated_VAT',
round((d1.amount + d2.amount )/1.18, 2) 'calculated_NET'
from transaction_details d1 join transaction_details d2 on d1.id = d2.netID
where
1 = 1
-- multiple conditions
) as x
The records selected into X table are 175, but when I run this query it goes over the entire transaction_details table (over 4.000.000 records).
I know that the order of operations in a query is
1. FROM clause
2. WHERE clause
3. GROUP BY clause
4. HAVING clause
5. SELECT clause
6. ORDER BY clause
so I was expecting the updated results to be constrained to the selected 175 records, but apparently it goes over the entire table and the result is 4.354.665 rows affected.
Is the order of operations different in a UPDATE statement, or I should redesign my query?
You need to directly reference the table to be updated in the FROM clause if you want any applied filters to work. Something like:
update d1 --Or d2?
set amount = (
CASE WHEN id = d1.netID /*?*/ THEN round((d1.amount + d2.amount )/1.18, 2)
WHEN id = d1.vatID /*?*/ THEN round(((d1.amount + d2.amount )/1.18) * 0.18, 2)
END
)
from
transaction_details d1 join transaction_details d2 on d1.id = d2.netID
where
1 = 1
-- multiple conditions
I'm not sure where netID or vatID are actually meant to come from since you didn't show them in your nested SELECT, and I've made a guess that the table to be updated is the d1 aliased one rather than d2. You can probably adapt from the above to hit your actual requirements.

Return id where count of value associated with that id on other table is 0

I have been stuck at a fairly simple scenario but even after scratching my head around for sometime I haven't been able to find a solution... Here's what I have.
I have got 2 tables with following data:
Trip (ID, Status)
30063 SUBMITTED
30066 SUBMITTED
30067 ASSIGNED
30068 SUBMITTED
And
AgentTripAssignment(TripId, AgentId, IsRejected)
30063 5 1
30063 2 0
30066 3 0
30066 4 0
30067 1 0
30067 2 0
30067 3 0
What I want to do is:
Return the trip id from trip table where status is SUBMITTED and if entry for trip is present in other table, count of IsRejected = 1 is zero in the AgentTripAssignment table with 1 query (as new enquiries....30066,30068 in the given case) and
Return the trip id from trip table where status is SUBMITTED and entry is present in AgentTripAssignment table having count of IsRejected = 1 appearing at least once in the table with second query (as agent rejected....30063 in the given case)
Other point of note is that the status of trip stays SUBMITTED until 3 agents are not assigned to a trip request which is when the status changes to ASSIGNED as is the case with 30067.
Any help will be much appreciated!
Case 1: Trip Submitted, No 'Rejected' Assignments
This is using a left join on assignments to handle the case of no assignments for a given trip. If the count of assignments in rejected state is not 0, it's excluded.
SELECT t.ID
FROM Trip AS t
LEFT JOIN AgentTripAssignmentx AS at
ON at.TripId = t.ID
WHERE t.Status = 'SUBMITTED'
GROUP BY t.ID
HAVING COUNT(CASE WHEN at.IsRejected = 1 THEN 1 ELSE NULL END) = 0
Case 2: Trip Submitted, Has 'Rejected' Assignments
Similar to above, but using an inner join, and excluding trips that don't have assignments rejected.
SELECT t.ID
FROM Trip AS t
JOIN AgentTripAssignmentx AS at
ON at.TripId = t.ID
WHERE t.Status = 'SUBMITTED'
GROUP BY t.ID
HAVING COUNT(CASE WHEN at.IsRejected = 1 THEN 1 ELSE NULL END) > 0
select a.tripid
from agenttripassignment a
join trip b on a.tripid = b.tripid
where b.status = 'submitted'
group by a.tripid
having max(a.isrejected) = 0
and likewise with 1
This is an example (in SQL Server syntax) that satisfies your first bullet point.
I broke the query down to try to explain the logic. (first 2 table statements are your data table samples)
WITH T AS ( -- Sample data
SELECT 30063 AS ID, 'Submitted' as Status
UNION ALL SELECT 30066, 'Submitted'
UNION ALL SELECT 30067, 'Assigned'
UNION ALL SELECT 30068, 'Submitted'
), A AS ( -- Sample data
SELECT 30063 AS ID, 5 AS AgentID, 1 AS IsRejected
UNION ALL SELECT 30063, 2, 0
UNION ALL SELECT 30066, 3, 0
UNION ALL SELECT 30066, 4, 0
UNION ALL SELECT 30067, 1, 0
UNION ALL SELECT 30067, 2, 0
UNION ALL SELECT 30067, 3, 0
), TPres AS ( -- get list of all IDs present in A table.
SELECT DISTINCT A.ID
FROM A
), RejCnt AS ( -- get List of all items with a rejected status
SELECT DISTINCT A.ID
FROM A
WHERE IsRejected=1
)
SELECT T.ID
FROM T
JOIN TPres ON TPres.ID=T.ID -- Filter out where entry for trip is in the other table.
LEFT JOIN RejCnt ON RejCnt.ID=T.ID -- Used to Determine if IsRejected is Zero
WHERE T.Status='Submitted' -- Where status=Submitted
AND RejCnt.ID IS NULL -- Has no rejected entries
This is very simply done with semi-joins (joins that filter rows but do not provide access to data and do not cause duplication of rows). For your first request, here is an anti-semi-join:
SELECT T.*
FROM
dbo.Trip T
WHERE
T.Status = 'SUBMITTED'
AND NOT EXISTS (
SELECT *
FROM dbo.AgentTripAssignment AT
WHERE
T.ID = AT.TripId
AND AT.IsRejected = 1
)
;
See a Live Demo at SQL Fiddle
This is very simply read as, "show me all rows from table Trip where no matching row (based on ID) exists in table AgentTripAssignment that has IsRejected = 1".
For your second query, it is almost exactly the same, simply change AND NOT EXISTS to EXISTS: "show all rows from table Trip where at least one matching row (based on ID) exists in table AgentTripAssignment that has IsRejected = 1".
The reason I prefer the EXISTS semi-join syntax is that it can often be better performance, depending on the exact indexes. It also helps database developers think in ways that I think are beneficial. It also has the benefit of not having to tack on a DISTINCT to fix duplication, and it avoids the problems of an aggregate method by allowing you to return all rows from the desired table (when a GROUP BY would have to contain all the columns in the table).

Resources