How to find destroyed data rows using audit trail with SQL - sql

I am using an audit table to find what records a single destructive SQL statement that was run on say '2011-01-01 08:00:00.000'. You can assume that each MyTable record only got updated once on this day the script ran. This SQL was probably run by a user and can not be retrieved. Everytime there is a change to MyTable an audit trail of the new value gets stored in AuditMyTable.
My original table schema:
MyTable(AccountId int PK, Amount int)
My audit trail table for MyTable schema:
AuditMyTable(AccountId int, Amount int, AuditDate datetime)
My question is how do I write a SQL join from AuditMyTable against itself so that I can get back the following data:
AuditMyTable.AccountId, AuditMyTable.Amount as [NullAmount], AuditMyTablePrevious.Amount as [PreviousNotNullAmount]

First, you have to get your sequence (I'm making one from your dates, assuming they are strictly increasing):
SELECT AccountId, cur.AuditDate AS cur_AuditDate, MAX(prev.AuditDate) AS prev_AuditDate
FROM AuditMyTable AS cur
LEFT JOIN AuditMyTable AS prev
ON prev.AccountId = cur.AccountId
AND prev.AuditDate < cur.AuditDate
GROUP BY AccountId, cur.AuditDate
Then use it:
WITH Ordering AS (
SELECT AccountId, cur.AuditDate AS cur_AuditDate, MAX(prev.AuditDate) AS prev_AuditDate
FROM AuditMyTable AS cur
LEFT JOIN AuditMyTable AS prev
ON prev.AccountId = cur.AccountId
AND prev.AuditDate < cur.AuditDate
GROUP BY AccountId, cur.AuditDate
)
SELECT cur.AccountId, cur.Amount as [NullAmount], prev.Amount as [PreviousNotNullAmount]
FROM AuditMyTable AS cur
INNER JOIN Ordering
ON Ordering.AccountId = cur.AccountId
AND Ordering.cur_AuditDate = cur.Audit_Date
LEFT JOIN AuditMyTable AS prev
ON prev.AccountId = cur.AccountId
AND prev.Audit_Date = Ordering.prev_AuditDate

Related

Duplicate Values in Query

Novice SQL user here - I am trying to determine the delivery date (de_arrdate) for an order based on event data from the events table. A shipment can have multiple events, shipments usually have 4 events so the events table will return data based on shipment ID for all 4 events. Because of this, my total $$$ is overstated. How can I return only the largest value of the shipment sequence which would essentially be the final event date? My query is below. I've also attached a sample of the current output.
select dba.disp_ship.ds_id, dba.disp_ship.ds_bill_charge,
dba.disp_ship.ds_status, dba.disp_ship.ds_ship_type,
dba.disp_events.de_site, dba.disp_events.de_arrdate,
dba.disp_events.de_shipment_id, dba.disp_events.de_ship_seq
from dba.disp_ship
inner join dba.disp_events on dba.disp_ship.ds_id = dba.disp_events.de_shipment_id
Not sure which RDBMS you are using nor the version, but if I understood correctly, you only want the amount stated in the last event of the sequence, right?
In this case, you already have the order of the events in the de_ship_seq column, so all you need to do is:
with last_event as (
select
de.de_arrdate,
de.de_shipment_id,
max(de.de_ship_seq)
from dba.disp_events as de
group by 1, 2
)
select
ds.ds_id,
ds.ds_bill_charge,
ds.de_arrdate
from dba.disp_ship as ds
join last_event as le on ds.ds_id = le.de_shipment_id
This way, you'll not get duplicity by the table disp_events, since you're only grabbing the maximum of the sequence, which it's supposed to be the last event :)
There are two ways to achieve this scenario.
1. Inner Query
select dba.disp_ship.ds_id, dba.disp_ship.ds_bill_charge,
dba.disp_ship.ds_status, dba.disp_ship.ds_ship_type,
dba.disp_events.de_site, dba.disp_events.de_arrdate,
dba.disp_events.de_shipment_id, dba.disp_events.de_ship_seq
from dba.disp_ship
inner join dba.disp_events on dba.disp_ship.ds_id = dba.disp_events.de_shipment_id,
inner Join (Select a.de_shipment_id as shipid,max(a.de_arrdate) as arrdate
from disp_events a) as t on dba.disp_events.de_shipment_id = t.shipid and dba.disp_events.de_arrdate = t.arrdate
2. Procedure
//Datatype for the Temporary tables is an assumption. Replace with your data type.
begin
declare local temporary table tbl1(
ds_id numeric(10),
ds_bill_charge numeric(14,2),
ds_status int,
ds_ship_type int,
de_site char(20),
de_arrdate date,
de_shipment_id numeric(10),
de_ship_seq numeric(10)
)on commit preserve rows;
declare local temporary table tbl1(
rowid numeric(10);
shipmentid numeric(10)
)on commit preserve rows;
declare #rowcount,#ds_id,i numeric(10);
set i = 1;
insert into tbl1
select dba.disp_ship.ds_id, dba.disp_ship.ds_bill_charge,
dba.disp_ship.ds_status, dba.disp_ship.ds_ship_type,
dba.disp_events.de_site, dba.disp_events.de_arrdate,
dba.disp_events.de_shipment_id, dba.disp_events.de_ship_seq
from dba.disp_ship
inner join dba.disp_events on dba.disp_ship.ds_id = dba.disp_events.de_shipment_id;
insert into tbl2
select number(*), ds_id from(select distinct ds_id from tbl1) a;
select count(*) into #rowcount from tbl2;
while i <= #rowcount Loop
Select ds_id into #ds_id from tbl2 where rowid = i;
delete from tbl1 where ds_id = #ds_id and
de_ship_seq not in(select top 1 de_ship_seq from tbl1 a
where a.ds_id = #ds_id order by de_arrdate desc);
i++;
end Loop;
select * from tbl1;
end
Thank You...

Using a Cursor inside a Stored Procedure to Write to a Table from Multiple Tables

I'm struggling to write my first procedure to pull data from multiple tables together and write it to another table using a cursor to loop through all of the data. Was hoping to find some help here.
I'm joining 6 tables back to the main table, Accounts, to displayed the required data. With the first cursor, I was joining five of the tables to get the needed information and then I wanted to add two more cursors to get phone numbers (primary and secondary) from a Phone Detail table added to the table.
Hopefully this makes sense. I'm sure I"m missing some thing in the SQL but basically I'd like to loop through the Accounts table and write the data into a new table and also loop through the Phone Detail table and get the primary phone for each account and then the secondary phone (while accounting for a NULL value) writing this to the new table as well.
CREATE PROCEDURE [dbo].[CRM_Account_Info]
#AccountID int,
#AccountName nvarchar(128),
#Bus_Type nvarchar(50),
#AccountAddr1 nvarchar(128),
#AccountAddr2 nvarchar(128),
#AccountCity nvarchar(32),
#AccountState nvarchar(10),
#AccountZip nvarchar(10),
#Account_Coll_Area_CodeID int,
#Account_Coll_Area nvarchar(50),
#Account_CRC_ID int,
#Account_CRC_Name nvarchar(100),
#Account_Prime_Number nvarchar(120),
#Account_2nd_Number nvarchar(120)
AS
BEGIN
-- Truncate Accounts table
Execute Immediate 'Truncate DBO.CRM_Accounts';
-- SET NOCOUNT ON added to prevent extra result sets from interfering with SELECT statements.
SET NOCOUNT ON;
-- Grab Account ID, Account Name, Account Type, Address, City, State, Zip, Collection Area ID, Collection Area Description,
-- Recruiter ID, Recruiter Full Name from the Accounts Table
Declare Acct_Info cursor for
Select
Acct.AccountID, Acct.Internalname,
QC.Descshort,
AD.Addr1, AD.Addr2, AD.City, AD.State, AD.Zip,
Sub.CodeID, Sub.Description,
Peo.PersonID, Peo.Fullname
from
Accounts as Acct
inner join
AddressDetail AD on Acct.AccountID = AD.AccountID
inner join
CenterDetail CD on Acct.Centerid = CD.CenterID
inner join
People Peo on Acct.LeaderID = Peo.PersonID
inner join
IDViewOrgSubCenter SUB on CD.OrgSubCenter = SUB.CodeID
inner join
QuickCodes QC on Acct.AccountType = QC.CodeID
Open Acct_Info -- Open cursor
Fetch Next from Acct_Info into #AccountID, #AccountName, #Bus_Type, #AccountAddr1,
#AccountAddr2, #AccountCity, #AccountState, #AccountZip,
#Account_Coll_Area_CodeID, #Account_Coll_Area,
#Account_CRC_ID, #Account_CRC_Name, #Account_Prime_Number,
#Account_2nd_Number
Close Acct_Info -- Close cursor
-- Grab the Primary Phone for the Account
Declare Primary_Phone cursor for
Select top 1 Acct.AccountID, PD.FormattedNumber
From PhoneDetail PD
inner join Accounts Acct on PD.AccountID=Acct.AccountID
Where PD.PrimaryPhone=1
And PD.AccountID=#AccountID
Close Primary_Phone -- Close cursor
-- Grab the second phone for an account
Declare Secondary_Phone cursor for
Select top 1 Acct.AccountID, PD.FormattedNumber
From PhoneDetail PD
inner join Accounts Acct on PD.AccountID=Acct.AccountID
Where PD.PrimaryPhone<>1
And PD.AccountID=#AccountID
Close Secondary_Phone -- Close cursor
-- Insert the values into the CRM table
Insert CRM_Accounts (
AccountID,
AccountName,
Bus_Type,
AccountAddr1,
AccountAddr2,
AccountCity,
AccountState,
AccountZip,
Account_Coll_Area_CodeID,
Account_Coll_Area,
Account_CRC_ID,
Account_CRC_Name,
Account_Prime_Number,
Account_2nd_Number
)
Values (
#AccountID,
#AccountName,
#Bus_Type,
#AccountAddr1,
#AccountAddr2,
#AccountCity,
#AccountState,
#AccountZip,
#Account_Coll_Area_CodeID,
#Account_Coll_Area,
#Account_CRC_ID,
#Account_CRC_Name,
#Account_Prime_Number,
#Account_2nd_Number
)
END
GO
Try this to get it all in one query. I did 2 derived tables one of primary and one of secondary and gave them row numbers partitioned by accountid. Then in the join I only get the rownumber = 1 so that it only returns one number per accountid.
SELECT Acct.accountid,
Acct.internalname,
QC.descshort,
AD.addr1,
AD.addr2,
AD.city,
AD.state,
AD.zip,
Sub.codeid,
Sub.description,
Peo.personid,
Peo.fullname,
pd1.formattedNumber,
pd2.formattedNumber
FROM accounts AS Acct
INNER JOIN addressdetail AD
ON Acct.accountid = AD.accountid
INNER JOIN centerdetail CD
ON Acct.centerid = CD.centerid
INNER JOIN people Peo
ON Acct.leaderid = Peo.personid
INNER JOIN idvieworgsubcenter SUB
ON CD.orgsubcenter = SUB.codeid
INNER JOIN quickcodes QC
ON Acct.accounttype = QC.codeid
LEFT JOIN (select accountid, formattednumber, primaryphone, row_number() over(partition by accountid order by formattednumber) as rNum
from phonedetail where primaryphone = 1) pd1
ON acct.accountid = pd1.accountid and pd1.rNum = 1
LEFT JOIN (select accountid, formattednumber, primaryphone, row_number() over(partition by accountid order by formattednumber) as rNum
from phonedetail where primaryphone <> 1) pd2
ON acct.accountid = pd2.accountid and pd2.rNum = 1
#JChao, I'm going to mark your answer correct as the SQL worked brilliantly but I'd like to post this here based on the recommendations received and for additional eyes to look over this procedure now. It seems to work but I'd just like more experienced users to check it out to see if they spot any glaring mistakes:
The first step is to drop (or truncate) all data currently in the table and then write the select statement into my new table:
Create Procedure Update_CRM_Accts
--Alter Procedure Update_CRM_Accts
As
Begin
Truncate Table CRM_Accounts
SET NOCOUNT ON;
Insert into CRM_Accounts
Select Acct.AccountID,
Acct.InternalName,
QC.DescShort,
AD.Addr1,
AD.Addr2,
AD.City,
AD.State,
AD.Zip,
Sub.CodeID,
Sub.Description,
Peo.PersonID,
Peo.FullName,
PD1.FormattedNumber as 'Primary_Number',
PD2.FormattedNumber as 'Secondary_Number'
From Accounts As Acct
INNER JOIN addressdetail AD ON Acct.AccountID = AD.AccountID
INNER JOIN CenterDetail CD ON Acct.CenterID = CD.CenterID
INNER JOIN People Peo ON Acct.LeaderID = Peo.PersonID
INNER JOIN IDViewOrgSubCenter SUB ON CD.OrgSubcenter = SUB.CodeID
INNER JOIN quickcodes QC ON Acct.AccountType = QC.CodeID
LEFT OUTER JOIN (Select AccountID, FormattedNumber, PrimaryPhone, row_number() over(partition by AccountID order by FormattedNumber) as rNum
From PhoneDetail where PrimaryPhone = 1) PD1
ON Acct.accountid = PD1.AccountID and PD1.rNum = 1
LEFT OUTER JOIN (Select AccountID, FormattedNumber, PrimaryPhone, row_number() over(partition by AccountID order by FormattedNumber) as rNum
From PhoneDetail where PrimaryPhone <> 1) PD2
ON Acct.AccountID = PD2.AccountID and PD2.rNum = 1
End
Go

Query returns a different result every time it is run

This query always returns the same amount of rows but, in a different order, every time. Why does this happen?
I have more filters to add but I can't get past this step.
BEGIN
DECLARE #lastStatus Varchar(10)
SELECT
[Job].[Job],
[Job].[Part_Number],
[Job].[Rev],
[Job_Operation].[Description],
[Job].[Customer_PO],
[Job].[Customer_PO_LN],
[Delivery].[Promised_Date],
[Job_Operation].[Operation_Service],
[Job].[Note_Text],
[Job_Operation].[Status],
[Job_Operation].[Sequence]
INTO [#tmpTbl]
FROM [PRODUCTION].[dbo].[Job_Operation]
INNER JOIN [Job]
ON [Job_Operation].[Job]=[Job].[Job]
INNER JOIN [Delivery]
ON [Job_Operation].[Job]=[Delivery].[Job]
WHERE [Job].[Status]='Complete'
ORDER BY [Job_Operation].[Job],[Job_Operation].[Sequence]
SELECT *
FROM [#tmpTbl]
DROP TABLE [#tmpTbl]
END
Put the Order By on the Select * From #tmpTbl, not on the insert.
Hi you can do initials on your table and you can remove your bracket for non spaces so you can make your code shorter.
SELECT j.Job,
,j.[Part_Number]
,j.Rev
,j_O.Description
,j.Customer_PO
,j.[Customer_PO_LN]
,d.[Promised_Date]
,j_o.[Operation_Service]
,j.[Note_Text],
,j_o.Status,
,j_o.Sequence
,j.[Customer_PO],
,j.[Customer_PO_LN],
,d.[Promised_Date],
,j_o.[Operation_Service],
,j.[Note_Text],
,j_o.[Status],
[Job_Operation].[Sequence]
INTO [#tmpTbl]
FROM [PRODUCTION].[dbo].[Job_Operation] j_o
INNER JOIN Job j
ON j_o.Job = j.Job
INNER JOIN Delivery d
ON j_o.Job= d.Job
WHERE j.Status='Complete'
ORDER BY j_o.Job,j_o.Sequence
SELECT *
FROM [#tmpTbl]
DROP TABLE [#tmpTbl]
END
Because you don't have an order by clause when you select from #tmpTbl
Try
SELECT *
FROM [#tmpTbl]
ORDER BY Job, Sequence
You cannot specify the order data goes into a table through a SET command (i.e. SELECT INTO) - that is determined by whether the table has a clustered index defined after it's created.
You control the order of the data when you're eventually selecting FROM that table to get your results.
SELECT * FROM [#tmpTbl] ORDER BY ....

Move data from SQL results to existing table

I'm a little bit stuck. I have a database table with the following columns:
Table Name: Data
*Value
*DateTime
*WeekNumber
*ItemId
*Name
I have created the following scripts from which I'd like to place the results into the above table.
SELECT D.*, M.Name
FROM
(SELECT SUM (Data) AS [Value],
(SELECT CONVERT(Date,DATEADD(week,-1,GETDATE()))) [DateTimeValue], DatePart (Week,TimestampUTC) [WeekNumber], MT.MeterId [MeterID]
FROM DataLog.dl
JOIN MeterTags mt ON dl.MeterTagId = mt.MeterTagId
GROUP BY DatePart (Week,TimestampUTC, dl.MeterTagId, MeterId)
AS D
INNER JOIN Meters m
ON D.MeterId = M.MeterId
ORDER BY MeterId DESC
I'm hoping to drop the results from the above query into the corresponding columns in the db table along with creating a new one for MeterID:
Value = Value
DateTime = DateTimeValue
WeekNumber = WeekNumber
MeterID = ***Need to create a new column*****
Name = Name
I hope this makes sense as I'm pretty inexperienced with SQL and a struggling to get the last pieces put together. Any help you can give would be greatly appreciated.
Thanks.
you can use the standard SQL INSERT syntax
INSERT INTO table2
SELECT * FROM table1;
what i don't understand is what do you mean by
MeterID = Need to create a new column**
for more info look
http://www.w3schools.com/sql/sql_insert_into_select.asp and
http://msdn.microsoft.com/en-us/library/ms188263(v=sql.105).aspx
Modify your table and add new column for MeterId.
Insert into [Data]
SELECT D.[Value], D.DateTimeValue,D.WeekNumber,MT.MeterId,M.Name
FROM
(SELECT SUM (Data) AS [Value],
(SELECT CONVERT(Date,DATEADD(week,-1,GETDATE()))) [DateTimeValue], DatePart (Week,TimestampUTC) [WeekNumber], MT.MeterId [MeterID]
FROM DataLog.dl
JOIN MeterTags mt ON dl.MeterTagId = mt.MeterTagId
GROUP BY DatePart (Week,TimestampUTC, dl.MeterTagId, MeterId)
AS D
INNER JOIN Meters m
ON D.MeterId = M.MeterId
ORDER BY MeterId DESC

linq to entities - try this one!

Im converting to linq to entities and I am finding problems attempting to convert the stored procs I have created as an overview of data.
How do I convert this sql statement to linq to entities:
I have a venue table with a child venuerooms table. With the last part I want to get the largest capacity for that venue across all rooms and roomtypes.
This is currently working in sql server 2005
Any help would be greately appreciated
ALTER proc [dbo].[sp_getVenueOverview]
(#venue varchar(100)) as
SELECT (Select accomrooms
from tblvenue
where venueid=(select venueid from tblvenue where urlfriendly = #venue))
as accomrooms,
(Select count(*)
from tblvenueroom
where venueid=(select venueid from tblvenue where urlfriendly = #venue))
as roomcount,
(Select Max(dbo.Greatest(theatrestyle,classroom,boardroom,ushape,banquet,cocktail))
from tblvenueroom
where venueid=(select venueid from tblvenue where urlfriendly = #venue))
as largest
you might want to refactor the query first, here is my try:
SELECT
v.accomrooms,r.roomcount,r.largest
FROM tblvenue v
LEFT OUTER JOIN (SELECT
v.venueid
,COUNT(*) AS roomcount
,Max(dbo.Greatest(r.theatrestyle,r.classroom,r.boardroom,r.ushape,r.banquet,r.cocktail) AS largest --dbo.Greatest() kills performance!
FROM tblvenue v
INNER JOIN tblvenueroom r ON v.venueid=r.venueid
WHERE v.urlfriendly = #venue
GROUP BY v.venueid
) dt ON v.venueid=dt.venueid
WHERE v.urlfriendly = #venue