Update Table with Duplicates - sql-server

My Table, #MetaData looks like this
Table_Name Element Join_prefix
Incident hold_reason h
Incident impact i
Incident incident_state i
Incident notify n
Incident severity s
Incident state s
Change impact i
Change incident_state i
I want to update the join_prefix where it is the same, to the first 2 characters of the element, within the Table_Name. So the table looks like
Table_Name Element Join_prefix
Incident hold_reason h
Incident impact im
Incident incident_state in
Incident notify n
Incident severity se
Incident state st
Change impact im
Change incident_state in
I've been using the following sql but it updates all the rows
update #MetaData
set join_prefix=substring(element,1,2)
where exists(
select [Table_Name],[Join_prefix]
from #MetaData
group by [Table_Name],[Join_prefix]
having count(join_prefix)>1)

One method would be to use an updatable CTE. Within the CTE you can use a windowed COUNT to count how many rows have the same prefix, and then update those rows:
SELECT *
INTO dbo.YourTable
FROM (VALUES('Incident','hold_reason ',CONVERT(varchar(4),'h')),
('Incident','impact ',CONVERT(varchar(4),'i')),
('Incident','incident_state',CONVERT(varchar(4),'i')),
('Incident','notify ',CONVERT(varchar(4),'n')),
('Incident','severity ',CONVERT(varchar(4),'s')),
('Incident','state ',CONVERT(varchar(4),'s')),
('Change ','impact ',CONVERT(varchar(4),'i')),
('Change ','incident_state',CONVERT(varchar(4),'i')))V(Table_Name,Element,Join_prefix)
GO
WITH CTE AS(
SELECT Element,
Join_prefix,
COUNT(Join_prefix) OVER (PARTITION BY Join_prefix) AS C
FROM dbo.YourTable)
UPDATE CTE
SET Join_prefix = LEFT(Element,2)
WHERE C > 1;
GO
SELECT *
FROM dbo.YourTable;
GO
DROP TABLE dbo.YourTable;

Your subquery is not correlated to the outside, so it always returns true. You need a WHERE
Note that exists doesn't need to select anything, you can select 1.
Also count(*) and count(non_null_value) is the same
update m1
set join_prefix = substring(element, 1, 2)
from #MetaData m1
where exists (select 1
from #MetaData m2
where m2.join_prefix = m1.join_prefix
group by m2.Table_Name, m2.Join_prefix
having count(*) > 1
);
A better method would be an updatable CTE
with CTE as (
select *,
cnt = count(*) over (partition by m.Table_Name, m.join_prefix)
from #MetaData m
)
update CTE
set join_prefix = substring(element,1,2)
from #MetaData m1
where t.cnt > 1;

Related

Compare previous date and current date and update the table on basis on condition

I want to update my attendance table on basis of the following condition.
NonWorking type is 1
If its previous day or next attendance type is Absent then I want to mark NonWorking type is LWP in DAOthers Column.
I think you can use LAG() and LEAD() here to peek at the preceding and proceeding values of the attendance type. Then, if one of those should be absent, mark the NonWorking column appropriately.
WITH cte AS (
SELECT *,
LAG(AttendanceType, 1, 'Present') OVER (ORDER BY ADate) AS lag_at,
LEAD(AttendanceType, 1, 'Present') OVER (ORDER BY ADate) AS lead_at
FROM yourTable
)
UPDATE cte
SET NonWorking = 1
WHERE lag_at = 'Absent' OR lead_at = 'Absent'
I am not sure whether you want an sql query to update existing data or a solution which is needed while making an entry.
Use below query to update existing data:
Update AttendanceTable set DaOthers =
(select top 1 'LWP' from AttendanceTable at1
where AttendanceTable.EmployeeId = at1.EmployeeId
and DATEADD(day, -1,AttendanceTable.ADate) = at1.ADate
and at1.NonWorking = 1)
Table befor executing above query:
Table after executing above query:
To update at the time of inserting record:
If you want to update while inserting data then you may need to set a variable first then use that variable while inserting. In the first query you need to use ADate and EmployeeID.The Nonworking is always 1.
DECLARE #DaOthers nvarchar(20) = (select top 1 'LWP' from AttendanceTable at
where DATEADD(day, 1, at.ADate) ='2017-02-04' and at.NonWorking = 1 and EmployeeId = 1)
insert into AttendanceTable
(NonWorking, ADate, AttendanceType, EmployeeId, DaOthers)
values
(0,'2017-02-04', 'Present', 1,#DaOthers)
With CTE as
(
SELECT *,
DATEADD(DAY, 1, Lag(ADate, 1,ADate)
OVER (PARTITION BY DAttendanceId ORDER BY ADate ASC)) AS EndDate
FROM tbl_DailyAttendance where EmployeeId = 1001 and AMonth = 2 and AYear = 2017 and AttendanceType = 'Absent' and NonWorking = 0
)
--select * from CTE
select * from tbl_DailyAttendance tda inner join CTE c on tda.ADate = c.EndDate where tda.EmployeeID = 1001 and tda.NonWorking = 1 order by tda.ADate
This is how i do for checking the conditions
since you hvn't provided sample data,so try to understand my query and correct if anything minor.
;WITH CTE as
(
select *
,isnull((select 1 from tbl_DailyAttendance tdb
where ((tdb.adate=DATEADD(day,-1,tda.adate))
or (tdb.adate=DATEADD(day,1,tda.adate)))
and attendancetype='Absent'
),0) NewnonWorkingType
from tbl_DailyAttendance tda
)
--Testing purpose
--select * from cte
update tda
set nonworking=b.NewnonWorkingType
,daOther=case when b.NewnonWorkingType=1 then 'LWP'
else null end
from tbl_DailyAttendance tda
inner join cte b on tda.id=b.id

Create a trigger update a field in a table from Total of Line Items in another table

I have two table Order and Order_Details. I would like to create a trigger that would update the Order.Order_Total by adding the Order_Details.Price fields that belong to that specific order. Here's what I have so far but its giving me the following error
Subquery returned more than 1 value. This is not permitted when the subquery follows
Update Order
Set Order_Total =
(Select SUM(Price)
From Order_Details
Group By Order_Id)
From Order_Details
Try this.. The issue is in your sub query which does not have any binding with the order table.
UPDATE o
SET o.Order_Total = t.tprice
FROM Order o
LEFT JOIN ( SELECT Order_Id, SUM(isnull(price,0)) tprice
FROM OrderDetails
GROUP BY Order_Id) t
ON o.Order_Id=t.Order_Id
Ok here's what I ended up doing in case someone has the same question. I created a CTE to add the Order_Details price and I updated the Order.Total from that CTE. Here's the full code I used.
IF EXISTS ( SELECT 1 FROM sys.triggers WHERE object_id = object_id('dbo.trOrder_Details_AIU') )
DROP TRIGGER dbo.trOrder_Details_AIU
GO
CREATE TRIGGER dbo.trOrder_Details_AIU
ON dbo.Order_Details
AFTER INSERT,UPDATE, Delete
AS
BEGIN
set nocount on;
begin
; with Total_CTE(Order_Id, Total)
as
(
Select Order_Id, SUM(Price)
From Order_Details
Group By Order_Id
)
Update Order
Set Order_Total = Total_CTE.Amount
From Total_CTE
Where Total_CTE.Order_Id = Total.Order_Id
end
END

Update Only Rows With Shared/Similar Value

OK - I have a simple table - below - what I am trying to do is to identify only those rows that have a shared value - so anything that has a shared "apc" value - DO x else Do Y
CREATE TABLE #test (hcpcs varchar(10), apc varchar(100), rate money)
INSERT INTO #test (hcpcs, apc)
SELECT '97110', '8009'
UNION ALL
SELECT '71020', '8009'
UNION ALL
SELECT '85025', '8006'
So - from the above - all those rows that share "8009" - I will gram those rows for an update - they will share the same "rate". Row with "8006" will not be a part of that update
You want:
WHERE EXISTS (SELECT *
FROM #test t2
WHERE t2.apc = #test.apc
AND t2.hcpcs != #test.hcpcs)
Make sure apc is indexed so that this can be done effectively.
IMO the most comfortable way to get all desired results is using SQL-Server's OVER Klause.
select *, count(*) over (partition by apc) as CNT
from #test order by CNT desc
will return all rows along with the duplicate information (order clause is just used so you can easily very this).
To turn this into an update, I'd use a CTE:
with T1 as (select hcpcs, count(*) over (partition by apc) as CNT
from #test)
update #test
set rate=#newrate
from #test inner join t1 on #test.hcpcs=T1.hcpcs
where CNT > 1
(please note that the order by clause had to vanish - it's not allowed in CTE and would be useless anyway :))
Just change whatever you want to the CASE expression. You can use the OUTPUT clause to check what it does, then remove it in the actual query.
UPDATE T
SET rate = CASE WHEN SRC.CNT > 1 THEN rate*5.00 ELSE rate*2.00 END
OUTPUT inserted.apc, deleted.rate old_rate, inserted.rate NEW_rate -- This is just to show results for testing
FROM #test T
JOIN (SELECT apc, COUNT(*) CNT
FROM #test
GROUP BY apc) SRC ON SRC.apc = T.apc

Update row based on previous row id

I have an update query like so
UPDATE myTable
SET ParentID = X
I need X to be the ID of the previous row that is currently being updated.
Any ideas?
If by "previous" row you mean the row where the id is the biggest value less than the value in the current row, you can use the lag() function in SQL Server 2012 or a correlated subquery:
UPDATE myTable
SET ParentID = (select top 1 id
from mytable m2
where m2.id < myTable.id
order by id desc
)
Maybe the following script will be useful (Assuming that the first value not have previous value then will be NULL), you can try this HERE:
CREATE TABLE TEST(
ID INT);
INSERT INTO TEST VALUES(10);
INSERT INTO TEST VALUES(20);
INSERT INTO TEST VALUES(30);
INSERT INTO TEST VALUES(40);
INSERT INTO TEST VALUES(50);
INSERT INTO TEST VALUES(60);
/*HERE THE SCRIPT*/
WITH temp AS (
SELECT x.ID,
ROW_NUMBER() over (order by x.ID) AS n
FROM TEST x
)
UPDATE t
SET t.ID = (SELECT temp.ID FROM temp WHERE temp.n = t.n - 1)
FROM (
SELECT x.ID,
ROW_NUMBER() over (order by x.ID) AS n
FROM TEST x
) t
SELECT * FROM TEST
NOTE: maybe this can be solved in an easier way, but it was the first thing that occurred to me with what you have posted

How do I select last 5 rows in a table without sorting?

I want to select the last 5 records from a table in SQL Server without arranging the table in ascending or descending order.
This is just about the most bizarre query I've ever written, but I'm pretty sure it gets the "last 5" rows from a table without ordering:
select *
from issues
where issueid not in (
select top (
(select count(*) from issues) - 5
) issueid
from issues
)
Note that this makes use of SQL Server 2005's ability to pass a value into the "top" clause - it doesn't work on SQL Server 2000.
Suppose you have an index on id, this will be lightning fast:
SELECT * FROM [MyTable] WHERE [id] > (SELECT MAX([id]) - 5 FROM [MyTable])
The way your question is phrased makes it sound like you think you have to physically resort the data in the table in order to get it back in the order you want. If so, this is not the case, the ORDER BY clause exists for this purpose. The physical order in which the records are stored remains unchanged when using ORDER BY. The records are sorted in memory (or in temporary disk space) before they are returned.
Note that the order that records get returned is not guaranteed without using an ORDER BY clause. So, while any of the the suggestions here may work, there is no reason to think they will continue to work, nor can you prove that they work in all cases with your current database. This is by design - I am assuming it is to give the database engine the freedom do as it will with the records in order to obtain best performance in the case where there is no explicit order specified.
Assuming you wanted the last 5 records sorted by the field Name in ascending order, you could do something like this, which should work in either SQL 2000 or 2005:
select Name
from (
select top 5 Name
from MyTable
order by Name desc
) a
order by Name asc
You need to count number of rows inside table ( say we have 12 rows )
then subtract 5 rows from them ( we are now in 7 )
select * where index_column > 7
select * from users
where user_id >
( (select COUNT(*) from users) - 5)
you can order them ASC or DESC
But when using this code
select TOP 5 from users order by user_id DESC
it will not be ordered easily.
select * from table limit 5 offset (select count(*) from table) - 5;
Without an order, this is impossible. What defines the "bottom"? The following will select 5 rows according to how they are stored in the database.
SELECT TOP 5 * FROM [TableName]
Well, the "last five rows" are actually the last five rows depending on your clustered index. Your clustered index, by definition, is the way that he rows are ordered. So you really can't get the "last five rows" without some order. You can, however, get the last five rows as it pertains to the clustered index.
SELECT TOP 5 * FROM MyTable
ORDER BY MyCLusteredIndexColumn1, MyCLusteredIndexColumnq, ..., MyCLusteredIndexColumnN DESC
Search 5 records from last records you can use this,
SELECT *
FROM Table Name
WHERE ID <= IDENT_CURRENT('Table Name')
AND ID >= IDENT_CURRENT('Table Name') - 5
If you know how many rows there will be in total you can use the ROW_NUMBER() function.
Here's an examble from MSDN (http://msdn.microsoft.com/en-us/library/ms186734.aspx)
USE AdventureWorks;
GO
WITH OrderedOrders AS
(
SELECT SalesOrderID, OrderDate,
ROW_NUMBER() OVER (ORDER BY OrderDate) AS 'RowNumber'
FROM Sales.SalesOrderHeader
)
SELECT *
FROM OrderedOrders
WHERE RowNumber BETWEEN 50 AND 60;
In SQL Server 2012 you can do this :
Declare #Count1 int ;
Select #Count1 = Count(*)
FROM [Log] AS L
SELECT
*
FROM [Log] AS L
ORDER BY L.id
OFFSET #Count - 5 ROWS
FETCH NEXT 5 ROWS ONLY;
Try this, if you don't have a primary key or identical column:
select [Stu_Id],[Student_Name] ,[City] ,[Registered],
RowNum = row_number() OVER (ORDER BY (SELECT 0))
from student
ORDER BY RowNum desc
You can retrieve them from memory.
So first you get the rows in a DataSet, and then get the last 5 out of the DataSet.
There is a handy trick that works in some databases for ordering in database order,
SELECT * FROM TableName ORDER BY true
Apparently, this can work in conjunction with any of the other suggestions posted here to leave the results in "order they came out of the database" order, which in some databases, is the order they were last modified in.
select *
from table
order by empno(primary key) desc
fetch first 5 rows only
Last 5 rows retrieve in mysql
This query working perfectly
SELECT * FROM (SELECT * FROM recharge ORDER BY sno DESC LIMIT 5)sub ORDER BY sno ASC
or
select sno from(select sno from recharge order by sno desc limit 5) as t where t.sno order by t.sno asc
When number of rows in table is less than 5 the answers of Matt Hamilton and msuvajac is Incorrect.
Because a TOP N rowcount value may not be negative.
A great example can be found Here.
i am using this code:
select * from tweets where placeID = '$placeID' and id > (
(select count(*) from tweets where placeID = '$placeID')-2)
In SQL Server, it does not seem possible without using ordering in the query.
This is what I have used.
SELECT *
FROM
(
SELECT TOP 5 *
FROM [MyTable]
ORDER BY Id DESC /*Primary Key*/
) AS T
ORDER BY T.Id ASC; /*Primary Key*/
DECLARE #MYVAR NVARCHAR(100)
DECLARE #step int
SET #step = 0;
DECLARE MYTESTCURSOR CURSOR
DYNAMIC
FOR
SELECT col FROM [dbo].[table]
OPEN MYTESTCURSOR
FETCH LAST FROM MYTESTCURSOR INTO #MYVAR
print #MYVAR;
WHILE #step < 10
BEGIN
FETCH PRIOR FROM MYTESTCURSOR INTO #MYVAR
print #MYVAR;
SET #step = #step + 1;
END
CLOSE MYTESTCURSOR
DEALLOCATE MYTESTCURSOR
Thanks to #Apps Tawale , Based on his answer, here's a bit of another (my) version,
To select last 5 records without an identity column,
select top 5 *,
RowNum = row_number() OVER (ORDER BY (SELECT 0))
from [dbo].[ViewEmployeeMaster]
ORDER BY RowNum desc
Nevertheless, it has an order by, but on RowNum :)
Note(1): The above query will reverse the order of what we get when we run the main select query.
So to maintain the order, we can slightly go like:
select *, RowNum2 = row_number() OVER (ORDER BY (SELECT 0))
from (
select top 5 *, RowNum = row_number() OVER (ORDER BY (SELECT 0))
from [dbo].[ViewEmployeeMaster]
ORDER BY RowNum desc
) as t1
order by RowNum2 desc
Note(2): Without an identity column, the query takes a bit of time in case of large data
Get the count of that table
select count(*) from TABLE
select top count * from TABLE where 'primary key row' NOT IN (select top (count-5) 'primary key row' from TABLE)
If you do not want to arrange the table in ascending or descending order. Use this.
select * from table limit 5 offset (select count(*) from table) - 5;

Resources