SQL Run query multiple times changing variable

1.7k Views Asked by At

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:

enter image description here

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:

enter image description here

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');
1

There are 1 best solutions below

8
On BEST ANSWER

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