How to properly JOIN three different queries - sql

I am using SQL Server 2008. I have three different queries that I would like to union together, but am unsure of how to properly go about it, as the structure of the queries are different.
My first query, creates two tables to insert records into for a comparison, where the MAX of a column for a visit is returned. Ex:
Query:
SET ANSI_NULLS OFF
GO
-- VARIABLE DECLARATION
DECLARE #STARTDATE DATETIME
DECLARE #ENDDATE DATETIME
-- VARIABLE INITIALIZATION
SET #STARTDATE = '6/1/12';
SET #ENDDATE = '1/1/13';
--#############################################################################################
-- TABLE DECLARATION WHICH WILL BE USED TO GET THE MAX ORDERED DATE #
-- #
DECLARE #T1 TABLE (ENCOUNTER VARCHAR(200), PT_NAME VARCHAR(500), MRN VARCHAR(200), --#
LOS VARCHAR(200), PT_LOC VARCHAR(500), PT_DISPO VARCHAR(500), --#
LAB_NAME VARCHAR(500), LAB_VALUE VARCHAR(40),LOWER_LIMIT VARCHAR(30), --#
UPPER_LIMIT VARCHAR(30), HISTORY VARCHAR(10), HAS_HISTORY VARCHAR(10), --#
AB_CODE VARCHAR(30), ORDER_ENTERED VARCHAR(500)) --#
DECLARE #T2 TABLE (ENCOUNTER2 VARCHAR(200), PT_NAME2 VARCHAR(500), MRN2 VARCHAR(200), --#
LOS2 VARCHAR(200), PT_LOC2 VARCHAR(500), PT_DISPO2 VARCHAR(500), --#
LAB_NAME2 VARCHAR(500), LAB_VALUE2 VARCHAR(40),LOWER_LIMIT2 VARCHAR(30), --#
UPPER_LIMIT2 VARCHAR(30), HISTORY2 VARCHAR(10), HAS_HISTORY2 VARCHAR(10),--#
AB_CODE2 VARCHAR(30), ORDER_ENTERED2 VARCHAR(500)) --#
-- #
--#############################################################################################
--## TABLE INSERTIONS ##
--###########################################################################################
--## WHAT GETS PUT INTO TABLE 1 ##
INSERT INTO #T1
SELECT
A.VisitIDCode,
A.ClientDisplayName,
A.IDCode,
A.LOS,
A.CurrentLocation,
A.DischargeDisposition,
A.ItemName,
A.Value,
A.ReferenceLowerLimit,
A.ReferenceUpperLimit,
A.IsHistory,
A.HasHistory,
A.AbnormalityCode,
A.Entered
FROM
(
-- COLUMN SELECTION
SELECT CV.VisitIDCode, CV.ClientDisplayName, CV.IDCode,DATEDIFF(DD,CV.ADMITDTM,CV.DISCHARGEDTM)AS 'LOS',
CV.CurrentLocation, CV.DischargeDisposition, BO.ItemName, BO.Value,
BO.ReferenceLowerLimit, BO.ReferenceUpperLimit, BO.IsHistory, BO.HasHistory, BO.AbnormalityCode,
BO.Entered
-- DB USED: SCM
FROM CV3ClientVisit CV
JOIN CV3BasicObservation BO
ON CV.GUID = BO.ClientVisitGUID
WHERE CV.AdmitDtm BETWEEN #STARTDATE AND #ENDDATE
AND CV.TypeCode = 'INPATIENT'
AND BO.Value IS NOT NULL
AND (BO.ItemName LIKE '%SODIUM LEVEL%'
OR BO.ITEMNAME LIKE '%HEMOG%')
)A
--## TABLE INSERTIONS ##
--###########################################################################################
--## WHAT GETS PUT INTO TABLE 2 ##
INSERT INTO #T2
SELECT
B.VisitIDCode,
B.ClientDisplayName,
B.IDCode,
B.LOS,
B.CurrentLocation,
B.DischargeDisposition,
B.ItemName,
B.Value,
B.ReferenceLowerLimit,
B.ReferenceUpperLimit,
B.IsHistory,
B.HasHistory,
B.AbnormalityCode,
B.Entered
FROM
(
-- COLUMN SELECTION
SELECT CV.VisitIDCode, CV.ClientDisplayName, CV.IDCode,DATEDIFF(DD,CV.ADMITDTM,CV.DISCHARGEDTM) AS 'LOS',
CV.CurrentLocation,CV.DischargeDisposition, BO.ItemName, BO.Value,BO.ReferenceLowerLimit, BO.ReferenceUpperLimit,
BO.IsHistory, BO.HasHistory, BO.AbnormalityCode,BO.Entered
-- DB USED: SCM
FROM CV3ClientVisit CV
JOIN CV3BasicObservation BO
ON CV.GUID = BO.ClientVisitGUID
WHERE CV.AdmitDtm BETWEEN #STARTDATE AND #ENDDATE
AND CV.TypeCode = 'INPATIENT'
AND BO.Value IS NOT NULL
AND (BO.ItemName LIKE '%SODIUM LEVEL%'
OR BO.ITEMNAME LIKE '%HEMOG%')
)B
--###########################################################################################
--## HERE IS WHERE WE DO TABLE COMPARISONS ##
SELECT
DISTINCT T1.ENCOUNTER,
T1.PT_NAME AS 'PT NAME', T1.MRN AS 'MRN', T1.LOS AS 'LOS', T1.PT_LOC AS 'PT LOC',
T1.PT_DISPO AS 'PT DISPO', T2.LAB_NAME2 AS 'LAB NAME', T2.LAB_VALUE2, T2.LOWER_LIMIT2 AS 'LOWER LIMIT',
T2.UPPER_LIMIT2 AS 'UPPER LIMIT', T2.AB_CODE2 AS 'AB CODE', T2.ORDER_ENTERED2
FROM #T1 T1
JOIN #T2 T2
ON T1.MRN = T2.MRN2
WHERE
T1.ENCOUNTER = T2.ENCOUNTER2
AND T1.ORDER_ENTERED < T2.ORDER_ENTERED2
AND T2.ORDER_ENTERED2 = (
SELECT MAX(TEMP.ORDER_ENTERED2)
FROM #T2 TEMP
WHERE T1.MRN = TEMP.MRN2
)
Results
CLIENT ID VISIT ID ARRIVE DATE VALUE DATE VALUE ORDERED
.......................................................................
1 | 1 | 1/1/13 | 5 | 1/1/13
1 | 1 | 1/1/13 | 6 | 1/2/13 <- returned row
Where this query will return the row with the date value ordered of 1/2/13.
The second query counts for me how many times the Client in the Client ID column came inside of a 12 month time frame. So for the above it would return something like
Query 2:
-- VARIABLE DECLARATION
DECLARE #STARTDATE DATETIME
DECLARE #ENDDATE DATETIME
-- INITIALIZE VARIABLES
SET #STARTDATE = '6/1/12';
SET #ENDDATE = '1/1/13';
-- COLUMN SELECTION
SELECT DISTINCT CV.IDCode AS 'MRN', COUNT(CV.IDCODE) AS 'COUNT OF IP VISITS'
FROM CV3ClientVisit CV
WHERE CV.AdmitDtm BETWEEN #STARTDATE AND #ENDDATE
AND CV.TypeCode LIKE '%INPATIENT'
AND CV.VisitStatus IN(
'ADM',
'DSC'
)
GROUP BY CV.IDCode
ORDER BY COUNT(CV.IDCode) DESC
Results:
Client ID Count of Visits
...............................
1 | 2
The third and final query tells me how many times they did something, for example
Query 3:
Declare #procedures Table (MRN varchar(20), Patient varchar(80), VisitID varchar(20), Admit datetime, Disch datetime, SurgProc varchar(200), ProcDesc varchar(200))
insert into #procedures
select cv.IDCode,cv.ClientDisplayName,cv.VisitIDCode,AdmitDtm,cv.DischargeDtm,ed.Description,ed.text from CV3ClientVisit cv
left join cV3ClientEventDeclaration ed
on cv.GUID=ed.ClientVisitGUID
where ed.typecode = 'Surgery'
and cv.AdmitDtm > '6/30/12' and cv.AdmitDtm <='1/1/13'
and Status = 'Active'
select visitid, COUNT(visitid)as '#Surg Procs' from #procedures
group by visitid
Results:
CLIENT ID VISIT ID ARRIVE DATE DEPART DATE COUNT OF PR VISITS
...............................................................................
1 | 1 | 1/1/13 | 1/3/13 | 3
So my question is, how do I properly JOIN all of these queries together in order to get just one result? There will be times where a client may not have a result for one of the queries.
I would like to final header to look something like this:
CLIENT ID | CLIENT VISIT ID | CLIENT NAME | LOS | PT LOC | PT DISPO | LAB NAME | LAB VALUE | LOWER LIMIT | UPPER LIMIT | AB CODE | COUNT OF IP VISITS | COUNT OF PROCEDURES
Thank You

OK. I'll give you a hint.
Let say you have two SELECT-type queries.
First returns: Last Name, First Name, Date of Birth.
Second returns: Last Name, First Name, Address, Occupation
You want the result as: Last Name, First Name, Occupation, Date of Birth, Address
You will do something like this:
SELECT LastName, FirstName, NULL, DOB, NULL FROM ...
UNION
SELECT LastName, FirstName, Occupation, NULL, Address FROM ...

Related

Subquery - Incorrect Syntax near the keyword 'declare'

Having an issue debugging this, hoping someone can help me clear this up. This is part of a much longer query, but the subquery "B" is the only part that is causing a problem, not sure why... I'm obviously missing something. I'm using sql-server.
The sub query runs fine on its own, just not with the rest of the query.
DECLARE #dstrt AS DATETIME
SET #dstrt = '2020-09-01 00:00:00'
DECLARE #dend AS DATETIME
SET #dend = '2020-09-30 23:59:59'
DECLARE #UnpaidChgsTot table
(
SiteID INT,
Period nvarchar(30),
dcDlqntTot money,
iDelUnits int,
dcPctUnits money,
dcPctGrossPot money,
dcPctActOcc money,
iDatePeriod int
)
DECLARE #sLanguageTermColName nvarchar(20)
SET #sLanguageTermColName = 'English'
-- DECLARE some period counters
DECLARE #StartDayNumber int
DECLARE #EndDayNumber int
SET #StartDayNumber = 0
SET #EndDayNumber = -1
--Hold the original date value
DECLARE #dEndORig datetime
SET #dEndORig = #dEnd
DECLARE #dcGrossPotDenom money
DECLARE #dcActOccDenom money
DECLARE #iTotUnitsDenom int
SET #dcGrossPotDenom = 1
SET #dcActOccDenom = 1
SET #iTotUnitsDenom = 1
--Define a holding table for charge balances by ChargeID
DECLARE #BalT table
(
SiteID int,
ChargeID int,
LedgerID int,
ChargeDescID int,
sChgCategory nvarchar(20),
sDefAcctCode nvarchar(20),
dChgStrt datetime,
dcBalAmt money,
dcBalTax1 money,
dcBalTax2 money
)
/*
* Updated. Delete was taking too long to run, added to WHERE to get rid
* Date: 10192009
* Josh
*/
--Fill the #BalT with charge balances for charges <=dEND
INSERT INTO #BalT
SELECT
B.SiteID,
B.ChargeID,
B.LedgerID,
B.ChargeDescID,
CD.sChgCategory,
CA.sDefAcctCode,
B.dChgStrt,
dcAmt,
dcTax1,
dcTax2
FROM -- PROBLEM STARTS HERE
(
DECLARE #ChargesT2 table
(
SiteID int,
ChargeID int,
ChargeDescID int,
sChgCategory nvarchar(50),
sDefAcctCode nvarchar(5),
dChgStrt datetime,
LedgerID int,
dcAmt money,
dcTax1 money,
dcTax2 money,
sChargeTag nvarchar(20)
)
DECLARE #fnPmtSumByChargeT Table
(
SiteID INT,
ChargeID int,
dcPmtSum money
)
DECLARE #fnPmtSumByChargeT2 Table
(
SiteID INT,
ChargeID int,
dcPmtSum money
)
DECLARE #ChargesTempT table
(
SiteID INT,
ChargeID int,
dcAmt money,
dcTax1 money,
dcTax2 money
)
Declare #ChargesT table
(
SiteID INT,
ChargeID int,
ChargeDescID int,
sChgCategory nvarchar(50),
sDefAcctCode nvarchar(5),
dChgStrt datetime,
LedgerID int,
dcAmt money,
dcTax1 money,
dcTax2 money,
sChargeTag nvarchar(20)
)
INSERT INTO #ChargesT2
SELECT
C.SiteID,
C.ChargeID,
C.ChargeDescID,
sChgCategory,
sDefAcctCode,
C.dChgStrt,
C.LedgerID,
Coalesce(C.dcAmt,0.0) AS dcBalAmt,
Coalesce(C.dcTax1,0.0) AS dcBalTax1,
Coalesce(C.dcTax2,0.0) AS dcBalTax2,
CASE
WHEN CAST(CA.sDefAcctCode AS INT) = 4000 THEN N'Rent'
WHEN CAST(CA.sDefAcctCode AS INT) = 4042 THEN N'LateFee'
WHEN CAST(CA.sDefAcctCode AS INT) = 4041 THEN N'AdminFee'
WHEN CAST(CA.sDefAcctCode AS INT) = 4070 THEN N'Insurance'
WHEN CAST(CA.sDefAcctCode AS INT) BETWEEN 4060 AND 4068 OR CAST(CA.sDefAcctCode AS INT) BETWEEN 4071 AND 4079 THEN N'POS'
--WHEN CAST(CA.sDefAcctCode AS INT) = 2020 THEN 'SecDep'
ELSE N'Others'
END as sChargeTag
FROM Charges AS C
INNER JOIN ChargeDesc AS CD ON C.ChargeDescID = CD.ChargeDescID
LEFT OUTER JOIN ChartOfAccts AS CA ON CD.ChartofAcctID = CA.ChartOfAcctID
WHERE
C.dDeleted IS NULL
AND C.dChgStrt <= #dend
--2016-01-12 - Case # 237424 - C158, L005, Unit 10478, Tenant - Zach Reese.
--Commented out dCreated evaluation. This is because A/R does not prevent backdating charges from changing historical reports. There is no way these two reports
--can tie out in the same period if we do not allow backdating charges that we created in a future period. Ex. Late fee created on January 2, 2016, but dChgStrt of 12/29/2015.
--AND dCreated <= #dEnd --This is to catch a case where NSF charges were added (backdated) after the report end date, affecting historical reporting. 02102011 Josh
AND (bNSF = 0 OR (bNSF = 1 AND dCreated <= #dend))--updated the logic to be different for NSF charges to not change reports historically; this loigc is consistant with A/R. Case # 269909
GROUP BY C.siteID, C.ChargeID, C.ChargeDescID, sChgCategory, sDefAcctCode, C.dChgStrt, C.LedgerID, C.dcAmt, C.dcTax1, C.dcTax2, CA.sDefAcctCode
INSERT INTO #fnPmtSumByChargeT2
SELECT
SiteID,
ChargeID,
dcPmtSum
FROM
(
SELECT
pay.SiteID,
Pay.ChargeID,
SUM(pay.dcPmtAmt) AS dcPmtSum
FROM
(
SELECT
SiteID,
ChargeID,
dcPmtAmt
FROM Payments
WHERE
(dDeleted Is Null)
--AND (bNSF = 0) --this fn must return ALL payments to calculate balances correctly
AND (dPmt <= #dend)
) AS Pay
GROUP BY SiteID, ChargeID
) AS P
INSERT INTO #fnPmtSumByChargeT2
SELECT
C.SiteID,
C.ChargeID,
0
FROM #ChargesT2 C
INSERT INTO #fnPmtSumByChargeT
SELECT
P.SiteID,
P.ChargeID,
SUM(P.dcPmtSum)
FROM #fnPmtSumByChargeT2 P
GROUP BY P.SiteID, P.ChargeID
INSERT INTO #ChargesTempT
SELECT
P.SiteID,
P.ChargeID,
C.dcAmt - Coalesce(dbo.fnPartNonTax(C.dcAmt, C.dcTax1, C.dcTax2, P.dcPmtSum, 2),0.0),
C.dcTax1 - Coalesce(dbo.fnPartTax1(C.dcAmt, C.dcTax1, C.dcTax2, P.dcPmtSum, 2,2),0.0),
C.dcTax2 - Coalesce(dbo.fnPartTax2(C.dcAmt, C.dcTax1, C.dcTax2, P.dcPmtSum,2,2),0.0)
FROM #ChargesT2 C
INNER JOIN #fnPmtSumByChargeT P ON C.ChargeID = P.ChargeID
GROUP BY P.SiteId, P.ChargeID, C.dcAmt, dcTax1, dcTax2, dcPmtSum
HAVING (dcAmt + dcTax1 + dcTax2 - dcPmtSum) > 0 -- Removed 0 sum columns: This was a major bottleneck for this function 10202009 J
INSERT INTO #ChargesT
SELECT
C.SiteID,
C.ChargeID,
C.ChargeDescID,
C.sChgCategory,
C.sDefAcctCode,
C.dChgStrt,
C.LedgerID,
CT.dcAmt,
CT.dcTax1,
CT.dcTax2,
C.sChargeTag
FROM #ChargesT2 C
INNER JOIN #ChargesTempT CT ON C.ChargeID = CT.ChargeID
SELECT *
FROM #ChargesT
AS ChargeT
) AS B -- PROBLEM ENDS HERE
INNER JOIN ChargeDesc AS CD ON B.ChargeDescID = CD.ChargeDescID AND B.Siteid = CD.SiteID
I indicated "Problem starts here" and "problem ends here" in the notes, if that helps.
Thanks in advance.
Inside a subquery the only thing you is write a select statement. You cannot write anything you want like it is a code block.
In your case, you may accomplish what you want by moving all those table variables to outside the parentheses and populating them, then inside the parentheses you just do the select. Like I said, it is not a code block, there are no local variables. There's no prettier way. SQL is a simple language for querying information. That's just how it is.
Alternatively, you may like to use Common Table Expressions (CTEs) to give each subquery a name. The subqueries do not exist as any temp variable or table, just as helper names available during the main query. That may solve what you want do to.

Inserting not calculated Id in table transact SQL

I have a stored procedure where I receive data as JSON from a API in C#. I insert the data into two tables like this:
INSERT INTO dbo.ServiceRequestHeader(SubscriptionId, CustomerAccountId, ModifiedBy)
OUTPUT Inserted.ServiceRequestHeaderId INTO #TempT
SELECT
SubscriptionId,
CustomerAccountId,
ModifiedBy
FROM
OpenJson(#JsonServiceRequest)
WITH
(SubscriptionId TinyInt,
CustomerAccountId Int)
SELECT #TempId = Id FROM #TempT
INSERT INTO dbo.ServiceRequest(ServiceRequestId, ServiceRequestHeaderId, SubscriptionId)
SELECT
#TempId, -- <= Here I need to modify the serviceRequestHeaderId
#TempId,
SubscriptionId
FROM
OpenJson(#JsonServiceRequest, '$.ServiceRequest')
WITH (SubscriptionId TinyInt,
...)
The thing is that the serviceRequestId is not a calculated field and it's a special case that depends on ServiceRequestHeaderId.
Example:
If ServiceRequestHeaderId = 1000 the ServiceRequestId would be 1000 001, 1000 002... N...
This is where I can't come with a way to do it
You can generate servicerequestids as given below. I am using FORMAT function with 000 for padding with 0 till 3 digits. If you want four digits, use 0000.
SELECT #TempId = Id FROM #TempT
INSERT INTO dbo.ServiceRequest(ServiceRequestId, ServiceRequestHeaderId, SubscriptionId)
SELECT
CONCAT(#TempId,FORMAT(ROW_NUMBER() OVER(ORDER BY (SELECT null)),'000')) AS ServiceRequestId, -- <= Here I need to modify the serviceRequestHeaderId
#TempId,
SubscriptionId
FROM
OpenJson(#JsonServiceRequest, '$.ServiceRequest')
WITH (SubscriptionId TinyInt,
...)
You will get something like below:
+------------------+
| ServiceRequestId |
+------------------+
| 1000001 |
| 1000002 |
| 1000003 |
+------------------+
Use a CTE to calculate a row number per request and then build the id from it e.g.
with MyCTE as (
select
SubscriptionId
-- Order by whatever makes business sense to you
, row_number() over (order by SubscriptionId) rn
from openjson(#JsonServiceRequest, '$.ServiceRequest')
with (
SubscriptionId tinyint,
...
)
)
insert into dbo.ServiceRequest (ServiceRequestId, ServiceRequestHeaderId, SubscriptionId)
-- Put whatever logic you like here to calculate a row number based id
select convert(varchar(4),#TempId) + ' ' + case when rn >= 100 then convert(varchar(3),rn) when rn > 10 then '0' + convert(varchar(2),rn) else '00' + convert(varchar(1),rn) end
, #TempId, SubscriptionId
from MyCTE;

SQL Run query multiple times changing variable

I have a query which checks the status of a specific tank, however I have 50 tanks which I'd like to run this for (FV101, FV102, FV103 etc.)
I could union together 50 queries with the WHERE changed, but I suspect that there must be a better way.
The query is very simple:
DECLARE #tank varchar(5)
SET #tank = 'FV101'
SELECT
ROW_NUMBER() OVER (PARTITION BY [Item] ORDER BY [TankName]) [Row],
*
FROM
(
SELECT TOP 1
#tank [TankName],
T1.[Item],
CASE WHEN AVG(CAST(T0.[pHValue] AS dec (10,5))) NOT BETWEEN MAX(T2. [pH_lower]) AND MAX(T2.[pH_upper]) THEN 'Red' ELSE 'Black' END [Spec]
FROM
t005_pci_data T0 INNER JOIN t001_fvbatch T1 ON T1.[FVBatch] = T0. [FVBatch] LEFT JOIN t024_specifications T2 ON T2.[BeerBrand] = T1.[BeerBrand] AND [Type] = 'Finished Product'
WHERE
T0.[FVBatch] = (SELECT TOP 1 T0.[FVBatch] FROM t005_pci_data T0 INNER JOIN t001_fvbatch T1 ON T1.[FVBatch] = T0.[FVBatch] WHERE T1.[TankName] = 'FV101' ORDER BY T0.[DateTime] DESC) AND
EXISTS (SELECT [FVBatch] FROM t005_pci_data WHERE [TankName] = #tank AND [DateTime] >= DATEADD(day,-2,GETDATE()))
GROUP BY
T1.[Item],T0.[DateTime]
ORDER BY
T0.[DateTime] DESC
) a
Is there a way to pass a list of values to this query for it to use instead of repeating the query manually multiple times?
EDIT as per iamdave's suggestion
Schema (simplified) is as below:
The goal is to list everything which is currently in each tank and check if the value of the pH is within the acceptable limits.
Desired output (up to FV105) would be:
In this example there is nothing in tanks FV101 or FV104, as decided by the following code in the WHERE
EXISTS (SELECT [FVBatch] FROM t005_pci_data WHERE [TankName] = #tank AND [DateTime] >= DATEADD(day,-2,GETDATE()))
The end result is I would like to create a table in MSSRS which shows what item is in each tank and whether it is within specifications or not.
FURTHER EDIT with sample data as requested
(Not very imaginative I'm afraid)
declare #t1 table(FVBatch int, TankName nvarchar(5), Item nvarchar(20));
declare #t2 table(Item nvarchar(20), ph_lower decimal(10,2), ph_upper decimal(10,2));
declare #t3 table(FVBatch int, pHValue decimal(10,2), DateValue datetime);
insert into #t1 values
(3160001,'FV101','Stout')
,(3160002,'FV102','Stout')
,(3160003,'FV103','Stout')
,(3160004,'FV104','Pale Ale')
,(3160005,'FV105','Pale Ale')
,(3160070,'FST04','IPA');
insert into #t2 values
('Pale Ale',3.5,5.5)
,('Stout',2,3.5);
insert into #t3 values
(3160001,4 ,'20161209')
,(3160001,4 ,'20161210')
,(3160001,4 ,'20161212')
,(3160002,4 ,'20161218')
,(3160002,4 ,'20161220')
,(3160002,4 ,'20161222')
,(3160003,4 ,'20161218')
,(3160003,4 ,'20161220')
,(3160003,4 ,'20161222')
,(3160004,4 ,'20161209')
,(3160004,4 ,'20161210')
,(3160004,4 ,'20161212')
,(3160005,4 ,'20161218')
,(3160005,4 ,'20161220')
,(3160005,4 ,'20161222')
,(3160070,4.26,'20161218')
,(3160070,4.26,'20161216')
,(3160070,4.24,'20161215')
,(3160070,4.24,'20161214')
,(3160070,4.26,'20161213')
,(3160070,4.2 ,'20161212')
,(3160070,4.21,'20161211')
,(3160070,4.12,'20161209')
,(3160070,4.09,'20161208')
,(3160070,4.1 ,'20161207');
How does this do?
select row_number() over (partition by t1.Item order by t1.TankName) as RowNum
,t1.TankName
,t1.Item
,case when avg(t3.pHValue) between t2.ph_lower and t2.ph_upper
then 'Black'
else 'Red'
end as Spec
from #t1 t1
left join #t2 t2
on(t1.Item = t2.Item)
inner join(select FVBatch
,pHValue
,max(DateValue) as DateValue
from #t3
where DateValue >= cast(dateadd(d,-2,getdate()) as date)
group by FVBatch
,pHValue
) t3
on(t1.FVBatch = t3.FVBatch)
group by t1.TankName
,t1.Item
,t2.ph_lower
,t2.ph_upper
order by t1.TankName
,RowNum
Using your test data, the above query returns:
RowNum TankName Item Spec
1 FV102 Stout Red
2 FV103 Stout Red
1 FV105 Pale Ale Black
Edit based on conversation
/*
The full requirement is this:
there could be multiple of each measurement taken on any one date,
so I want to check that the average of these measurements per day is withing the range
each batch goes through several stages
if the stage isn't "bottle" or "can" then we look back 2 days
if it is one of those then we look back 5 days instead
*/
declare #t001_fvbatch table(FVBatch int
,TankName nvarchar(5)
,BeerBrand nvarchar(20)
);
declare #t024_specifications table(BeerBrand nvarchar(20)
,pH_lower decimal(10,2)
,pH_upper decimal(10,2)
,OG_lower decimal(10,2)
,OG_upper decimal(10,2)
,PG_lower decimal(10,2)
,PG_upper decimal(10,2)
,EBCHaze decimal(10,2)
,ABV_lower decimal(10,2)
,ABV_upper decimal(10,2)
,[Type] nvarchar(50)
);
declare #t005_pci_data table(FVBatch int
,pHValue decimal(10,2)
,Alcohol decimal(10,2)
,OG decimal(10,2)
,PG decimal(10,2)
,EBCHaze decimal(10,2)
,[DateTime] datetime
,Stage nvarchar(20)
,TankName nvarchar(20)
);
select b.FVBatch
,b.TankName
,b.BeerBrand
,case when((d.Stage in('CAN','BOTTLE','BBT')
and avg(cast(d.EBCHaze as dec(10,5))) > 5
)
or (avg(cast(d.Alcohol as dec(10,5))) not between max(s.ABV_lower) and max(s.ABV_upper)
or avg(cast(d.OG as dec(10,5))) not between max(s.OG_lower) and max(s.OG_upper)
or avg(cast(d.PG as dec(10,5))) not between max(s.PG_lower) and max(s.PG_upper)
or avg(cast(d.pHValue as dec(10,5))) not between max(s.pH_lower) and max(s.pH_upper)
)
)
then 'Red'
else 'Black'
end as Spec
from #t001_fvbatch b -- Always start at the table with the most central piece of data. In this case, the specifications and measurements all relate to a single batch.
left join #t024_specifications s
on(b.BeerBrand = s.BeerBrand
and s.[Type] = 'Finished Product'
)
inner join (select d2.FVBatch -- This sub select returns the most recent DateTime value per each FVBatch and TankName combination.
,d2.TankName
,cast(max(d2.[DateTime]) as date) as MostRecentMeasurement -- Cast/convert to DATE type to remove the TIME element. This means 2016-12-22 12:00:00 and 2016-12-22 13:59:43 both become 2016-12-22.
from #t005_pci_data d2
where d2.[DateTime] >= cast(dateadd(d -- This case statement filters the DateTime values by a number of days based on the value in the Stage column.
,case when d2.Stage in('can','bottle')
then -5
else -2
end
,getdate()
)
as date)
group by d2.FVBatch
,d2.TankName
) dm
on(b.FVBatch = dm.FVBatch
and b.TankName = dm.TankName
)
inner join #t005_pci_data d -- We then join into the data table again to get all the measurements taken on the same day as the most recent.
on(b.FVBatch = d.FVBatch
and b.TankName = d.TankName
and d.[DateTime] >= dm.MostRecentMeasurement -- Filtering like this allows any indexes on your DateTime column to be used to speed execution time. (This is called sargability).
and d.[DateTime] < dateadd(d,1,dm.MostRecentMeasurement) -- We could use functions to convert the DateTime to a DATE data type to match the MostRecentMeasurement value, but this would then need to be calculated for every single measurement in the table, which is wasteful.
)
-- Using INNER JOIN for the measurements means we are only return batches that have measurements that meet our filtering criteria.
-- If we wanted to return a batch even if there were no measurements matching, we would use LEFT JOIN instead.
group by b.FVBatch
,b.TankName
,b.BeerBrand
,d.Stage

SQL: create new table with distinct value and max/min values for other columns w/in an existing table

Example:
Suppose the original table contains the following values
AcctNbr StatusDate
------------------
123 01/01/2012
123 01/01/2013
123 12/11/2011
987 01/01/2009
The SQL would create a new table containing
AcctNbr EarliestStatusDate LatestStatusDate
-------------------------------------------
123 12/11/2011 01/01/2013
987 01/01/2009 01/01/2009
I'm looking for an efficient way to do this. I have a method that works, but it takes an unacceptably long time. Does anyone have any optimization tips. Any help would be greatly appreciated.
SET NOCOUNT ON
DROP TABLE loopTemp
DROP TABLE reportTemp
CREATE TABLE loopTemp
( ID int IDENTITY(1, 1) NOT NULL,
AcctNbr varchar(50),
)
CREATE TABLE reportTemp
(
AcctNbr varchar(50),
EarliestStatus Date,
LatestStatus Date
)
INSERT INTO loopTemp
SELECT DISTINCT AcctNbr
FROM AutoStatusHistory
DECLARE #COUNTER AS INT
SET #COUNTER = 1
DECLARE #MAX AS INT
SET #MAX = (SELECT MAX(ID) FROM loopTemp)
WHILE #COUNTER < #MAX BEGIN
DECLARE #ACCOUNT_NUMBER AS varchar(50)
SET #ACCOUNT_NUMBER =
(SELECT AcctNbr FROM loopTemp WHERE ID = #COUNTER)
DECLARE #EARLIESTSTATUSDATE AS DATE
SET #EARLIESTSTATUSDATE = (SELECT MIN(NewStatusDate)
FROM AutoStatusHistory
WHERE AcctNbr = #ACCOUNT_NUMBER)
DECLARE #LATESTSTATUSDATE AS DATE
SET #LATESTSTATUSDATE = (SELECT MAX(NewStatusDate)
FROM AutoStatusHistory
WHERE AcctNbr = #ACCOUNT_NUMBER)
INSERT INTO reportTemp
VALUES (#ACCOUNT_NUMBER, #EARLIESTSTATUSDATE, #LATESTSTATUSDATE)
IF (#COUNTER % 1000) = 0 BEGIN
PRINT #COUNTER
END -- IF
SET #COUNTER = #COUNTER + 1
END -- WHILE
DROP TABLE loopTemp
Unless I'm missing something, this should be extremely simple:
SELECT AcctNbr, MIN(StatusDate) AS EarliestStatusDate,
MAX(StatusDate) AS LatestStatusDate
FROM myTable
GROUP BY AcctNbr
You appear to be using SQL Server. For this, you can do:
select AcctNbr, min(StatusDate) as EarliestStatusDate,
max(StatusDate) as LatestStatusDate
into ReportTemp
from AutoStatusHistory
group by AcctNbr
The into statement saves the results into a table. You might want to use an actual temporary table, with the syntax:
into #ReportTemp
You can use
SELECT *
INTO TableName
FROM (
SELECT AcctNbr, MIN([Status Date]) AS EarliestStatusDate,
MAX([Status Date]) AS LatestStatusDate
FROM myTable
GROUP BY AcctNbr
) RequiredData

sql query help - trying to get rid of temp tables

I have the following tables -
Resource
--------------------
Id, ProjectId, Hours, ApproverId
Project
--------------------
Id, Name
The input is ApproverId. I need to retrieve all the rows that have matching ApproverId (simple enough). And for every resource that I get back, I also need to get their hours (same table) whose approverId is not the one that is passed in (business requirement, to be grayed out in the UI). What I'm doing right now is - get all resources based on ApproverId, stored them in a temp table, then do a distinct on Resource.Id, store it in a different temp table, and then for every Resource.Id, get the rows where the ApproverId is not the one that is passed. Can I combine it all in a single query instead of using temp tables?
Thanks!
Edit: I'm using SQL Server 2008 R2.
Edit 2: Here's my stored procedure. I have changed the logic slightly after reading the comments. Can we get rid of all temp tables and make it faster -
ALTER PROCEDURE GetResourceDataByApprover
#ApproverId UNIQUEIDENTIFIER
AS
CREATE TABLE #Table1
(
Id SMALLINT PRIMARY KEY
IDENTITY(1, 1) ,
ResourceId UNIQUEIDENTIFIER
)
CREATE TABLE #Table2
(
ResourceId UNIQUEIDENTIFIER ,
ProjectId UNIQUEIDENTIFIER ,
ProjectName NVARCHAR(1024)
)
INSERT INTO #Table1
SELECT DISTINCT
ResourceId
FROM dbo.Resource T
WHERE T.ApproverId = #ApproverId
DECLARE #i INT
DECLARE #numrows INT
DECLARE #resourceId UNIQUEIDENTIFIER
SET #i = 1
SET #numrows = ( SELECT COUNT(*)
FROM #Table1
)
IF #numrows > 0
WHILE ( #i <= ( SELECT MAX(Id)
FROM #Table1
) )
BEGIN
SET #resourceId = ( SELECT ResourceId
FROM #Table1
WHERE Id = #i
)
INSERT INTO #Table2
SELECT
T.ResourceId ,
T.ProjectId ,
P.Name AS ProjectName
FROM dbo.[Resource] T
INNER JOIN dbo.Project P ON T.ProjectId = P.ProjectId
WHERE T.ResourceId = #resourceId
SET #i = #i + 1
END
SELECT *
FROM #Table1
SELECT *
FROM #Table2
DROP TABLE #Table1
DROP TABLE #Table2
This query should return two rows for every resource, one for the specified approver and one for all other approvers.
SELECT
Id,
CASE
WHEN ApproverId=#approverId THEN 'SpecifiedApprover'
ELSE 'OtherApprover'
END AS Approver,
SUM(Hours) AS Hours
FROM Resource
GROUP BY
Id,
CASE
WHEN ApproverId=#approverId THEN 'SpecifiedApprover'
ELSE 'OtherApprover'
END
Do you want to know how concrete Approver wastes his time?
SELECT p.Id, p.Name, SUM(r.Hours) as TotalHours
FROM Resource r
LEFT JOIN Project p
ON r.ProjectId = p.Id
WHERE ApproverId = %ConcreteApproverId%
GROUP BY p.Id, p.Name
HAVING SUM(r.Hours) > 0
This query will produce this table example:
+-----+----------+-------+
| Id | Project | Hours |
+-----+----------+-------+
| 203 | ProjectA | 25 |
| 202 | ProjectB | 34 |
| 200 | ProjectC | 46 |
+-----+----------+-------+