`SQL challenge` I need to create blended benchmar...
# sql
j
SQL challenge
I need to create blended benchmarks which are made up of components and weights. ( details here are not important ) If a blend is already in the system, I want to retrieve its benchmarkID. If not, I'm going to create a set of new records that make up the blend. There may be many blends that use the same components, but with differing weights. Given the following array, what do you suppose is the most efficient/elegant way to find a match? A single sql query would be ideal, but using qb.get() (or queryexecute() returning an array of structs) and arrayEach() is acceptable too.
Copy code
//  the table name is benchmarkBlends and the rows we're trying to match look like this:

[
	{
		"benchmarkID": 46,
		"componentID": 11,
		"pctWeight": 52.2
	},
	{
		"benchmarkID": 46,
		"componentID": 13,
		"pctWeight": 20.0
	},
	{
		"benchmarkID": 46,
		"componentID": 22,
		"pctWeight": 27.8
	}
]

// the incoming blend looks like this:
[
	{
		"componentID": 11,
		"pctWeight": 52.2
	},
	{
		"componentID": 13,
		"pctWeight": 20.0
	},
	{
		"componentID": 22,
		"pctWeight": 27.8
	}
]
m
Are you using SQL Server if so I would put the incoming values into a table variable... and since the assumption that all the components sum up to 100 then if you do a select and there is no match then you would insert into whatever table has your benchmarkID and then use that to insert the components....
Copy code
DECLARE @benchmarks as table
--CREATE TABLE @benchmarks
 (benchmarkID int, label varchar(max))
DECLARE @benchmarkBlends as table
---CREATE TABLE @benchmarkBlends
    (benchmarkID int, componentID int, pctWeight float);

INSERT INTO @benchmarks (benchmarkid, label)  VALUES (46, 'label')

INSERT INTO @benchmarkBlends
    (benchmarkID, componentID, pctWeight)
VALUES
 (46, 11, 52.2),
 (46, 13, 20.0),
 (46, 22, 27.8)


DECLARE @tmp as table(componentID int, pctWeight float)
INSERT INTO @tmp (componentID, pctWeight) VALUES (11, 53.2), (13, 19.0), (22, 27.8)
DECLARE @benchmarkID as int
DECLARE @tot as float
SELECT @tot = sum(t.pctWeight), @benchMarkID = max(bb.benchmarkID) FROM @tmp t inner join @benchmarkBlends bb on t.componentID = bb.componentID and t.pctWeight = bb.pctWeight

if ( @tot != 100 ) 
BEGIN
	INSERT INTO @benchmarks (label) VALUES ('NewLabel')
    SET @benchmarkID = @@IDENTITY
	INSERT INTO @benchMarkBlends (benchmarkID, componentID, pctWeight)
    SELECT @benchmarkID, t.componentID, t.pctWeight FROM @tmp t
   
END

PRINT @benchmarkID
SELECT * FROM @benchmarkBlends
j
@Michael Schmidt interesting solution. The problem is that blends could use the same combination of benchmarks but with differing weights.
m
That solutions hould cover that... that is why the @tmp coming in is of different then what is stored...
j
tricky problem to solve
m
to prove that it works for both cases
j
I see
making my eyes cross after a long day but this is an interesting solution
g
Reuse part of Michael's code, to find a match, we could also use GROUP BY
Copy code
DECLARE @benchmarkBlends as table (benchmarkID int, componentID int, pctWeight float);

INSERT INTO @benchmarkBlends
    (benchmarkID, componentID, pctWeight)
VALUES
 (46, 11, 52.2),
 (46, 13, 20.0),
 (46, 22, 27.8),
 (47, 10, 50.2),
 (47, 9, 25.0),
 (47, 8, 24.8),
 (48, 12, 50.0),
 (48, 15, 25.0),
 (48, 16, 25.0),
 (50, 11, 33.3),
 (50, 13, 33.3),
 (50, 29, 33.4)

SELECT benchmarkID
FROM @benchmarkBlends
WHERE
	(componentID = 11 AND pctWeight = 52.2) OR 
	(componentID = 13 AND pctWeight = 20.0) OR 
	(componentID = 22 AND pctWeight = 27.8)
GROUP BY
	benchmarkID
Having
	sum(pctWeight) = 100
If there is data return from the SQL, then there is a match, if there are more than one records return, then there are multiple matches. Borrowed Michael's idea, just a different SQL.
s
bit late, you could generate a json representation for the blend makeup and use that for the check
Copy code
with benchmark as (
 select 46 benchmarkID , 11 componentID, cast(52.2 as Decimal(5,1)) pctWeight
  union all
 select 46, 13, 20
  union all
 select 46, 22, 27.8
),
benchmarkjson as (
SELECT
  benchmarkID,
  (select componentID, pctWeight from benchmark i where i.benchmarkID = b.benchmarkID order by componentID for json path) json,
  convert(varchar,HASHBYTES('SHA2_256',(select componentID, pctWeight from benchmark i where i.benchmarkID = b.benchmarkID order by componentID for json path)),2) jsonhash
FROM
  benchmark b
 group by benchmarkID
)


select * 
from benchmarkjson 
where 1=1
  and json = '[{"componentID":11,"pctWeight":52.2},{"componentID":13,"pctWeight":20.0},{"componentID":22,"pctWeight":27.8}]'
  --and jsonhash = 'DB3C1CE5BAD0423FFF598CB5C9D893' --could use a hash of the json