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

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

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...

TSQL not pulling in complete data set

I have a complicated stored procedure that worked great until the client wanted to change it.
I am not great with complicated TSQL so I have no idea what is wrong with my code.
Here is the situation. I have three temp tables, Cost, Adjustments, and Payments.
In the end I merge all these tables together in a report table. The problem I am having is even if one or even two of these tables are null, as long as one table has data I need that data to show. I currently have it set up with full outer joins but I'm still not getting the full list, I'm missing probably....50 ish records that should be there.
Can anyone look at this code and tell me what the heck I'm doing wrong? I'm bringing all the data together on #ThisReportAll
UPDATE: So I removed the having clause to see what was going on, and the data for the overdue balance is returning null. So the math isn't...mathing correctly, any ideas?
CODE
CREATE TABLE #BalanceAdjustmentsAll (CustomerId int, Amount decimal(20,2));
CREATE TABLE #AnimalCostsAll (thisIndex int IDENTITY(1,1), AnimalTypeId int, Cost decimal(20,2));
CREATE TABLE #TotalAnimalCostAll (thisIndex int IDENTITY(1,1), YearSetupId int, AnimalTypeId int, AnimalType varchar(max), OwnerId int, CustomerId int, AnimalCount int, TtlSpeciesCost decimal(20,2));
CREATE TABLE #CustomerPaymentsAll (thisIndex int IDENTITY(1,1), CustomerID nvarchar(max), TtlPayments decimal(20,2));
CREATE TABLE #CustomerInfoAll (thisIndex int IDENTITY(1,1), OwnerId int, CustomerId int, FName nvarchar(200), LName nvarchar(200),BName nvarchar(200));
CREATE TABLE #ThisReportAll (thisIndex int IDENTITY(1,1), CustomerID nvarchar(max), Year char(4), OverdueBalance decimal(20,2), YearSetupId int);
INSERT INTO #BalanceAdjustmentsAll (CustomerId, Amount)
SELECT CustomerId, SUM(Amount)
FROM BalanceAdjustment
WHERE YearSetupId = 3
GROUP BY CustomerId;
/* GET Costs per Animal for 'This' yearID */
INSERT INTO #AnimalCostsAll (AnimalTypeId, Cost)
SELECT AnimalTypeId, Cost
FROM PerCapitaFee
WHERE YearSetupId = 3;
/* GET animal type totals for owner per year */
INSERT INTO #TotalAnimalCostAll (yearSetupId,AnimalTypeId,AnimalType,OwnerId,CustomerId,AnimalCount,TtlSpeciesCost)
SELECT YearSetup.YearSetupId,AnimalCount.AnimalTypeId,AnimalType.ShortDescription,Owner.OwnerId,Report.CustomerId,AnimalCount.Count,(ac.Cost * AnimalCount.Count)
FROM AnimalCount
INNER JOIN #AnimalCostsAll as ac
ON ac.AnimalTypeId = AnimalCount.AnimalTypeId
INNER JOIN AnimalType
ON AnimalCount.AnimalTypeId=AnimalType.AnimalTypeId
INNER JOIN AnimalLocation
ON AnimalLocation.AnimalLocationid=AnimalCount.AnimalLocationId
INNER JOIN Owner
ON Owner.OwnerId=AnimalLocation.OwnerId
AND Owner.OwnerType = 'P'
INNER JOIN Report
ON Report.ReportId=Owner.ReportId
INNER JOIN YearSetup
ON Report.YearSetupId=YearSetup.YearSetupId
INNER JOIN County
ON County.CountyId=AnimalLocation.CountyId
WHERE YearSetup.YearSetupId = 3 AND Report.Completed IS NOT NULL AND Report.CustomerId IS NOT NULL
/* Get The total payments a customer has made */
INSERT INTO #CustomerPaymentsAll (CustomerID,TtlPayments)
SELECT BPS.CustomerId,SUM(BPS.Amount)
FROM BatchPaymentSplit BPS
LEFT JOIN BatchPayment bp ON BPS.BatchPaymentId=bp.BatchPaymentId
LEFT JOIN Batch b ON bp.BatchId=b.BatchId
WHERE BPS.CustomerId IS NOT NULL
AND
(
((b.BatchTypeId = 'M' OR b.BatchTypeId = 'C' OR b.BatchTypeId = 'E') AND (b.BatchStatusId = 'S'))
OR
((b.BatchTypeId = 'B' OR b.BatchTypeId = 'N' OR b.BatchTypeId = 'R' OR b.BatchTypeId = 'T') AND (b.BatchStatusId = 'S' OR b.BatchStatusId='C'))
)
AND
BPS.YearSetupId = 3
GROUP BY BPS.CustomerId;
/* Deal with the name/id stuff */
INSERT INTO #CustomerInfoAll(FName, LName, BName, OwnerId, CustomerId)
SELECT
o.FirstName AS FName,
o.LastName AS LName,
o.BusinessName AS BName,
o.OwnerId AS OwnerId,
r.CustomerId AS CustomerId
FROM Owner o
INNER JOIN Report r
ON o.ReportId = r.ReportId
AND o.OwnerType = 'P'
WHERE r.CustomerId IN (SELECT CustomerId FROM #TotalAnimalCostAll)
AND r.Completed IS NOT NULL
AND r.YearSetupId = 3
AND NOT EXISTS(
SELECT 1 FROM Report
WHERE r.CustomerId = Report.CustomerId
AND Report.Completed IS NOT NULL
AND r.ReportId != Report.ReportId
AND r.YearSetupId = Report.YearSetupId
AND (
r.Completed < Report.Completed
OR (
r.Completed = Report.Completed
AND r.ReportId < Report.ReportId
)
)
)
ORDER BY CustomerId;
/** MAKE IT SO #1 **************************************************/
/* Simply Joining The Customer Info to the calculated totals to avoid any aggregation shenanigans... */
INSERT INTO #ThisReportAll (CustomerID,Year,OverdueBalance,YearSetupId)
SELECT COALESCE(t.CustomerId,cp.CustomerId,ba.CustomerID), ys.Name AS Year,
CASE
WHEN (SUM(t.TtlSpeciesCost) < 5 AND SUM(t.TtlSpeciesCost) > 0) AND (ys.Name='2015' OR ys.Name='2016')
THEN (5) - Isnull(cp.TtlPayments,0) + Isnull(ba.Amount,0)
ELSE SUM(t.TtlSpeciesCost) - Isnull(cp.TtlPayments,0) + Isnull(ba.Amount,0)
END
AS TtlOwnerCost, t.YearSetupId AS YearSetupId
FROM #TotalAnimalCostAll t
FULL OUTER JOIN #CustomerPaymentsAll cp ON t.CustomerId=cp.CustomerID
FULL OUTER JOIN #BalanceAdjustmentsAll ba ON COALESCE(t.CustomerId,cp.CustomerId)=ba.CustomerID
LEFT JOIN YearSetup ys ON COALESCE(t.CustomerId,cp.CustomerId,ba.CustomerID) = ys.YearSetupId
GROUP BY COALESCE(t.CustomerId,cp.CustomerId,ba.CustomerID),ys.Name,cp.TtlPayments, ba.Amount, t.YearSetupId
HAVING
CASE WHEN (SUM(t.TtlSpeciesCost) < 5 AND SUM(t.TtlSpeciesCost) > 0) AND (ys.Name='2015' OR ys.Name='2016')
THEN SUM(5) - Isnull(cp.TtlPayments,0) + Isnull(ba.Amount,0)
ELSE SUM(t.TtlSpeciesCost) - Isnull(cp.TtlPayments,0) + Isnull(ba.Amount,0)
END < 0;
/* Return some meaningful report data */
SELECT r.Year AS [YearName],r.CustomerID,left(ci.FName,20) AS [FirstName], left(ci.LName,40) AS [LastName], left(ci.BName,40) AS [BusinessName],r.OverdueBalance AS [Balance],r.YearSetupId
FROM #ThisReportAll r
LEFT JOIN #CustomerInfoAll ci ON r.CustomerID = ci.CustomerId
ORDER BY CAST(r.CustomerID as int) ASC;
DROP TABLE #BalanceAdjustmentsAll;
DROP TABLE #AnimalCostsAll;
DROP TABLE #TotalAnimalCostAll;
DROP TABLE #CustomerPaymentsAll;
DROP TABLE #CustomerInfoAll;
DROP TABLE #ThisReportAll;
Found it. I didn't have a default value for t.TtlSpeciesCost if it was null
SUM(t.TtlSpeciesCost) - Isnull(cp.TtlPayments,0) + Isnull(ba.Amount,0)
to
SUM(ISNULL(t.TtlSpeciesCost,0)) - Isnull(cp.TtlPayments,0) + Isnull(ba.Amount,0)
Some missing records may be found here:
by adjusting /* Get The total payments a customer has made */
INSERT INTO #CustomerPaymentsAll (CustomerID,TtlPayments)
SELECT BPS.CustomerId,SUM(BPS.Amount)
FROM BatchPaymentSplit BPS
LEFT JOIN BatchPayment bp
ON BPS.BatchPaymentId=bp.BatchPaymentId
LEFT JOIN Batch b
ON bp.BatchId=b.BatchId
AND ((b.BatchTypeId IN ('M', 'C', 'E') AND b.BatchStatusId = 'S')
OR (b.BatchTypeId IN ('B','N','R','T') AND (b.BatchStatusId IN ('S','C')))
WHERE BPS.CustomerId IS NOT NULL
AND BPS.YearSetupId = 3
GROUP BY BPS.CustomerId;
The WHERE on B would have negated the left join causing null records to be omitted. or made the left join to behave like an inner join.
To know for certain we need sample data from your tables showing which records are being omitted that you need to retain.
I also refactored the OR's and made them "IN"s to improve readability.

Merge two rows with condition SQL View

I have a View which has a SQL Script as:
Select
a.iAssetId,
ac.eEventCode,
vm.dtUTCDateTime,
g.iGeofenceId,
g.sGeofenceName,
c.sCategoryName,
c.iCategoryId,
s.sSiteName,
s.iSiteId,
CASE WHEN ac.eEventCode = 6 THEN vm.dtUTCDateTime ELSE NULL END as EnterTime,
CASE WHEN ac.eEventCode = 7 THEN vm.dtUTCDateTime ELSE NULL END as ExitTime,
CASE WHEN
a.iAssetId = Lead(a.iAssetId) OVER (ORDER BY a.iAssetId)
AND g.iGeofenceId = Lead(g.iGeofenceId) OVER (ORDER BY a.iAssetId)
AND ac.eEventCode != Lead(ac.eEventCode) OVER (ORDER BY a.iAssetId)
THEN DATEDIFF(minute, vm.dtUTCDateTime, Lead(vm.dtUTCDateTime) OVER (ORDER BY a.iAssetId)) ELSE NULL END as Test
From AssetCommunicationSummary ac
Inner join VehicleMonitoringLog vm on vm.iVehicleMonitoringId = ac.iVehicleMonitoringId
Inner Join Geofences g on g.iGeofenceId = vm.iGeofenceId
Inner Join Assets a on a.iAssetId = ac.iAssetId
Inner Join Categories c on c.iCategoryId = a.iCategoryId
Inner Join Sites s on s.iSiteId = c.iSiteId
Where ac.eEventCode = 6 OR ac.eEventCode = 7
Group by
a.iAssetId,
ac.eEventCode,
vm.dtUTCDateTime,
g.iGeofenceId,
g.sGeofenceName,
c.sCategoryName,
c.iCategoryId,
s.sSiteName,
s.iSiteId
I have used Lead to calculate the Time differenc in minutes for leading rows based on conditions.
I need to now merge the leading Row and the Current Row based on Condition.
Is there a possible way to do this?
The goal is to get the EnterTime and ExitTime in the Same Row with Time Difference in the Column Next to it.
My result is like this:
If your eventcode is always going to be 6 and 7, then you can just join to that table twice using that clause in the join itself. I think I've got the rest of your schema joined up properly below, but if not, you can adjust it around to fit.
Select
a.iAssetId,
vmEnter.dtUTCDateTime,
g.iGeofenceId,
g.sGeofenceName,
c.sCategoryName,
c.iCategoryId,
s.sSiteName,
s.iSiteId,
vmEnter.dtUTCDateTime as EnterTime,
vmExit.dtUTCDateTime as ExitTime,
DATEDIFF(minute, vmEnter.dtUTCDateTime, vmExit.dtUTCDateTime) as ExitTime,
From Sites s
Inner Join Categories c on s.iSiteId = c.iSiteId
Inner Join Assets a on c.iCategoryId = a.iCategoryId
Inner Join AssetCommunicationSummary acEnter on a.iAssetId = acEnter.iAssetId and acEnter.eEventCode = 6
Inner Join VehicleMonitoringLog vmEnter on vmEnter.iVehicleMonitoringId = acEnter.iVehicleMonitoringId
Inner Join AssetCommunicationSummary acExit on a.iAssetId = acExit.iAssetId and acExit.eEventCode = 7
Inner Join VehicleMonitoringLog vmExit on vmExit.iVehicleMonitoringId = acExit.iVehicleMonitoringId
Inner Join Geofences g on g.iGeofenceId = vmEnter.iGeofenceId
You can use this ddl to test and see the idea of what is going on. It's copy and paste ready, if you want to see a difference in times, make sure you wait before you insert each record.
Create table testing
(
Id int ,
Enter DateTime,
Exitt DateTime,
Eventt int,
GeoCode int
)
insert into testing values (1, GETDATE(),null,6,10)
insert into testing values (1, null,GETDATE(),7,10)
insert into testing values (1, GETDATE(),null,6,11)
insert into testing values (1, null,GETDATE(),7,11)
insert into testing values (2, GETDATE(),null,6,10)
insert into testing values (2, null,GETDATE(),7,10)
create table #temp1
(
Id int, EnterTime datetime, GeoCode int
)
create table #temp2
(
Id int, ExitTime datetime, GeoCode int
)
insert into #temp1
Select Id, MAX(Enter), GeoCode from testing where Eventt = 6 group by Id,GeoCode
insert into #temp2
Select Id, MAX(Exitt),GeoCode from testing where Eventt = 7 group by Id,GeoCode
Select t1.Id, t1.EnterTime,t2.ExitTime, t1.GeoCode, DATEDIFF(ss,t1.EnterTime,t2.ExitTime)
from #temp1 t1
inner join #temp2 t2 on t2.Id = t1.Id
and t1.GeoCode = t2.GeoCode
This is basically pseudo code so your going to need to modify, but everything you need is here.
Im gonna guess that eventcode = 6 means thats the intake time
if so two of your data paris dont make much sense as the exit time is before the intake time,
The Query below only accounts for when amd if eventcode 6 = intake time
and the fact that exit time should be before entertime.
query is based on the output you provided and not the view query.
if doing a select * on your view table gives you that output then replace vw_table with yourviewstablename
There are Nulls in the timedif of sqlfiddle because
there was only one instance of assetid 2
assetid 4 and 6 have exit time that happened before entertimes
SQLFIDDLE
select
v1.iAssetid,
v1.EnterTime,
v2.ExitTime,
datediff(minute, v1.Entertime, v2.Exittime) timedif
from vw_table v1
left join vw_table v2 on
v1.iAssetid= v2.iAssetid
and v1.sCategoryNamea = v2.sCategoryNamea
and v2.eEventcode = 7
and v2.dtUTCDatetime >= v1.dtUTCDatetime
where
v1.eEventcode = 6
You can merge two result sets by adding Row_Number to them and then join on that. Like
SELECT DISTINCT tbl1.col1, tbl2.col2
FROM
(SELECT FirstName AS col1, ROW_NUMBER() OVER (ORDER BY FirstName) Number FROM dbo.UBUser) tbl1
INNER JOIN
(SELECT LastName AS col2, ROW_NUMBER() OVER (ORDER BY LastName) Number FROM dbo.UBUser) tbl2
ON tbl1.Number = tbl2.Number
This way you will be able to have EnterTime and ExitTime in the Same Row with Time Difference in the Column Next to it.
Try this
SELECT iAssetid,
iGeoFenceId,
iGeoFenceName,
sCategoryNamea,
iCategoryid,
sSiteName,
Max(EnterTime) As EnterTime,
Min(ExitTime) As ExitTime,
Datediff(minute, Max(EnterTime), Min(ExitTime)) As Timediff
FROM #vw_Table
GROUP BY iAssetid,
iGeoFenceId,
iGeoFenceName,
sCategoryNamea,
iCategoryid,
sSiteName

How can I perform the Count function with a where clause?

I have my database setup to allow a user to "Like" or "Dislike" a post. If it is liked, the column isliked = true, false otherwise (null if nothing.)
The problem is, I am trying to create a view that shows all Posts, and also shows a column with how many 'likes' and 'dislikes' each post has. Here is my SQL; I'm not sure where to go from here. It's been a while since I've worked with SQL and everything I've tried so far has not given me what I want.
Perhaps my DB isn't setup properly for this. Here is the SQL:
Select trippin.AccountData.username, trippin.PostData.posttext,
trippin.CategoryData.categoryname, Count(trippin.LikesDislikesData.liked)
as TimesLiked from trippin.PostData
inner join trippin.AccountData on trippin.PostData.accountid = trippin.AccountData.id
inner join trippin.CategoryData on trippin.CategoryData.id = trippin.PostData.categoryid
full outer join trippin.LikesDislikesData on trippin.LikesDislikesData.postid =
trippin.PostData.id
full outer join trippin.LikesDislikesData likes2 on trippin.LikesDislikesData.accountid =
trippin.AccountData.id
Group By (trippin.AccountData.username), (trippin.PostData.posttext), (trippin.categorydata.categoryname);
Here's my table setup (I've only included relevant columns):
LikesDislikesData
isliked(bit) || accountid(string) || postid(string
PostData
id(string) || posttext || accountid(string)
AccountData
id(string) || username(string)
CategoryData
categoryname(string)
Problem 1: FULL OUTER JOIN versus LEFT OUTER JOIN. Full outer joins are seldom what you want, it means you want all data specified on the "left" and all data specified on the "right", that are matched and unmatched. What you want is all the PostData on the "left" and any matching Likes data on the "right". If some right hand side rows don't match something on the left, then you don't care about it. Almost always work from left to right and join results that are relevant.
Problem 2: table alias. Where ever you alias a table name - such as Likes2 - then every instance of that table within the query needs to use that alias. Straight after you declare the alias Likes2, your join condition refers back to trippin.LikesDislikesData, which is the first instance of the table. Given the second one in joining on a different field I suspect that the postid and accountid are being matched on the same row, therefore it should be AND together, not a separate table instance. EDIT reading your schema closer, it seems this wouldn't be needed at all.
Problem 3: to solve you Counts problem separate them using CASE statements. Count will add the number of non NULL values returned for each CASE. If the likes.liked = 1, then return 1 otherwise return NULL. The NULL will be returned if the columns contains a 0 or a NULL.
SELECT trippin.PostData.Id, trippin.AccountData.username, trippin.PostData.posttext,
trippin.CategoryData.categoryname,
SUM(CASE WHEN likes.liked = 1 THEN 1 ELSE 0 END) as TimesLiked,
SUM(CASE WHEN likes.liked = 0 THEN 1 ELSE 0 END) as TimesDisLiked
FROM trippin.PostData
INNER JOIN trippin.AccountData ON trippin.PostData.accountid = trippin.AccountData.id
INNER JOIN trippin.CategoryData ON trippin.CategoryData.id = trippin.PostData.categoryid
LEFT OUTER JOIN trippin.LikesDislikesData likes ON likes.postid = trippin.PostData.id
-- remove AND likes.accountid = trippin.AccountData.id
GROUP BY trippin.PostData.Id, (trippin.AccountData.username), (trippin.PostData.posttext), (trippin.categorydata.categoryname);
Then "hide" the PostId column in the User Interface.
Instead of selecting Count(trippin.LikesDislikesData.liked) you could put in a select statement:
Select AccountData.username, PostData.posttext, CategoryData.categoryname,
(select Count(*)
from LikesDislikesData as likes2
where likes2.postid = postdata.id
and likes2.liked = 'like' ) as TimesLiked
from PostData
inner join AccountData on PostData.accountid = AccountData.id
inner join CategoryData on CategoryData.id = PostData.categoryid
USE AdventureWorksDW2008R2
GO
SET NOCOUNT ON
GO
/*
Default
*/
SET TRANSACTION ISOLATION LEVEL READ COMMITTED
GO
BEGIN TRAN
IF OBJECT_ID('tempdb.dbo.#LikesDislikesData') IS NOT NULL
BEGIN
DROP TABLE #LikesDislikesData
END
CREATE TABLE #LikesDislikesData(
isLiked bit
,accountid VARCHAR(50)
,postid VARCHAR(50)
);
IF OBJECT_ID('tempdb.dbo.#PostData') IS NOT NULL
BEGIN
DROP TABLE #PostData
END
CREATE TABLE #PostData(
postid INT IDENTITY(1,1) NOT NULL
,accountid VARCHAR(50)
,posttext VARCHAR(50)
);
IF OBJECT_ID('tempdb.dbo.#AccountData') IS NOT NULL
BEGIN
DROP TABLE #AccountData
END
CREATE TABLE #AccountData(
accountid INT
,username VARCHAR(50)
);
IF OBJECT_ID('tempdb.dbo.#CategoryData') IS NOT NULL
BEGIN
DROP TABLE #CategoryData
END
CREATE TABLE #CategoryData(
categoryname VARCHAR(50)
);
INSERT INTO #AccountData VALUES ('1', 'user1')
INSERT INTO #PostData VALUES('1','this is a post')
INSERT INTO #LikesDislikesData (isLiked ,accountid, postid)
SELECT '1', P.accountid, P.postid
FROM #PostData P
WHERE P.posttext = 'this is a post'
SELECT *
FROM #PostData
SELECT *
FROM #LikesDislikesData
SELECT *
FROM #AccountData
SELECT COUNT(L.isLiked) 'Likes'
,P.posttext
,A.username
FROM #PostData P
JOIN #LikesDislikesData L
ON P.accountid = L.accountid
AND L.IsLiked = 1
JOIN #AccountData A
ON P.accountid = A.accountid
GROUP BY P.posttext, A.username
SELECT X.likes, Y.dislikes
FROM (
(SELECT COUNT(isliked)as 'likes', accountid
FROM #LikesDislikesData
WHERE isLiked = 1
GROUP BY accountid
) X
JOIN
(SELECT COUNT(isliked)as 'dislikes', accountid
FROM #LikesDislikesData
WHERE isLiked = 0
GROUP BY accountid) Y
ON x.accountid = y.accountid)
IF (XACT_STATE() = 1 AND ERROR_STATE() = 0)
BEGIN
COMMIT TRAN
END
ELSE IF (##TRANCOUNT > 0)
BEGIN
ROLLBACK TRAN
END
How do you think about the solution? We create a new table SummaryReport(PostID,AccountID,NumberOfLikedTime,NumberOfDislikedTimes).
An user clicks on LIKE or DISLIKE button we update the table. After that, you can query as you desire. Another advantage, the table can be served reporting purpose.

How to find destroyed data rows using audit trail with 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