I have two tables I want to join.
Table A has one column, named "Week", and contains 52 rows: 1,2,3,4,5,6 etc.
Table 2 has three columns, named "Name", "Week", and "Total", and contains 10 rows:
'Bob', 1, 1
'Bob', 3, 1
'Joe', 4, 1
'Bob', 6, 1
I want to join these together so that my data looks like:
NAME|WEEK|TOTAL
'Bob', 1, 1
'Bob', 2, 0
'Bob', 3, 1
'Bob', 4, 0
'Bob', 5, 0
'Bob', 6, 1
As you can see, a simple outer join. However, when I try to do this, I'm not getting the expected result, no matter what kind of join I use.
My query below:
SELECT a.WEEK, b.Total
FROM Weeks a LEFT JOIN Totals b ON (a.Week = b.Week and b.Name ='Bob')
The result of this query is
NAME|WEEK|TOTAL
'Bob', 1, 1
'Bob', 3, 1
'Bob', 6, 1
Thanks in advance for the help!
I know its access but your join is incorrect. Here we go in sql server..same concept just look at the join condition:
--dont worry about this code im just creating some temp tables
--table to store one column (mainly week number 1,2..52)
CREATE TABLE #Weeks
(
weeknumber int
)
--insert some test data
--week numbers...I'll insert some for you
INSERT INTO #Weeks(weeknumber) VALUES(1)
INSERT INTO #Weeks(weeknumber) VALUES(2)
INSERT INTO #Weeks(weeknumber) VALUES(3)
INSERT INTO #Weeks(weeknumber) VALUES(4)
INSERT INTO #Weeks(weeknumber) VALUES(5)
INSERT INTO #Weeks(weeknumber) VALUES(6)
--create another table with two columns storing the week # and a total for that week
CREATE TABLE #Table2
(
weeknumber int,
total int
)
--insert some data
INSERT INTO #Table2(weeknumber, total) VALUES(1, 100)
--notice i skipped week 2 on purpose to show you the results
INSERT INTO #Table2(weeknumber, total) VALUES(3, 100)
--here's the magic
SELECT t1.weeknumber as weeknumber, ISNULL(t2.total,0) as total FROM
#Weeks t1 LEFT JOIN #Table2 t2 ON t1.weeknumber=t2.weeknumber
--get rid of the temp tables
DROP TABLE #table2
DROP TABLE #Weeks
Results:
1 100
2 0
3 100
4 0
5 0
6 0
Take your week number table (the table that has one column:
SELECT t1.weeknumber as weeknumber
Add to it a null check to replace the null value with a 0. I think there is something in access like ISNULL:
ISNULL(t2.total, 0) as total
And start your join from your first table and left join to your second table on the weeknumber field. The result is simple:
SELECT t1.weeknumber as weeknumber, ISNULL(t2.total,0) as total FROM
#Weeks t1 LEFT JOIN #Table2 t2 ON t1.weeknumber=t2.weeknumber
Do not pay attention to all the other code I have posted, that is only there to create temp tables and insert values into the tables.
SELECT b.Name, b.Week, b.Total
FROM Totals AS b
WHERE b.Name ='Bob'
UNION
SELECT 'Bob' AS Name, a.Week, 0 AS Total
FROM Weeks AS a
WHERE NOT EXISTS ( SELECT *
FROM Totals AS b
WHERE a.Week = b.Week
AND b.Name ='Bob' );
You were on the right track, but just needed to use a left join. Also the NZ function will put a 0 if total is null.
SELECT Totals.Person, Weeks.WeekNo, Nz(Totals.total, 0) as TotalAmount
FROM Weeks LEFT JOIN Totals
ON (Weeks.WeekNo = Totals.weekno and Totals.Person = 'Bob');
EDIT: The query you now have won't even give the results you've shown because you left out the Name field (Which is a bad name for a field because it is a reserved word.). You're still not providing all the information. This query works.
*Another Approach: * Create a separate query on the Totals table having a where clause: Name = 'Bob'
Select Name, WeekNo, Total From Totals Where Name = 'Bob';
and substitute that query for the Totals table in this query.
Select b.Name, w.WeekNo, b.total
from Weeks as w
LEFT JOIN qryJustBob as b
on .WeekNo = b.WeekNo;
Related
I'm trying to get the average of two values which are in two different tables. I only want to get the average of the values where the in the same column the 'Week' Values of both tables are the same.
So e.g.:
Table1 Name= BicepsTable
Week | Biceps
1 | 33
2 | 33.2
3 | 34.1
.
Table2 Name=ThighTable
Week | Thigh
1 | 42.1
3 | 42.8
4 | 43
.
From these tables I want to have the values {(1, 37.55), (3, 38.45)}.
( . (33+42)/2=37.55 . . . . (34.1+42.8)/2=38.45 . )
I tried to get this with the following code but the following code gives me {(1, 37.55), (3, 37.55)} where the second value is wrong, the second average value should be the one of the next column.
sql = 'SELECT BicepsTable.Week,
((SELECT BicepsTable.Biceps FROM BicepsTable INNER JOIN ThighTable ON BicepsTable.Week = ThighTable.Week)
+
(SELECT ThighTable.Thigh FROM ThighTable INNER JOIN BicepsTable ON ThighTable.Week = BicepsTable.Week)) /2
FROM BicepsTable INNER JOIN ThighTable ON BicepsTable.Week = ThighTable.Week'
Please help, if you don't understand my problem, or got questions, feel free to ask:)
I suggest taking a union of the two tables, and then taking the average of each week:
SELECT Week, AVG(rating) AS avg_rating
FROM
(
SELECT Week, Biceps AS rating FROM BicepsTable
UNION ALL
SELECT Week, Thigh FROM ThighTable
) t
GROUP BY Week
HAVING COUNT(*) = 2
ORDER BY Week;
Aggregation, as used above, is a good option here, because the AVG will only operate on the values which are present. So, if only one or the other table has a value, then the average will reflect that.
declare #test1 as table
(id int,t1value float)
declare #test2 as table
(id int,t2value float)
insert into #test1
values(1,100),(2,150),(3,200)
insert into #test2
values(1,100),(3,150),(5,200)
select *,(a.t1value+b.t2value)/2 taverage from #test1 a
inner join #test2 b on a.[id]=b.[id]
group by a.id,a.t1value,b.id,b.t2value
I have my table in this structure. I am trying to find all the unique ID's whose word's do not appear in the list. How can I achieve this in MS SQL Server.
id word
1 hello
2 friends
2 world
3 cat
3 dog
2 country
1 phone
4 eyes
I have a list of words
**List**
phone
eyes
hair
body
Expected Output
Except the words from the list, I need all the unique ID's. In this case it is,
2
3
I & 4 is not in the output as their words appears in the List
I tried the below code
Select count(distinct ID)
from Table1
where word not in ('phone','eyes','hair','body')
I tried Not Exists also which did not work
You can also use GROUP BY
SELECT id
FROM Table1
GROUP BY id
HAVING MAX(CASE WHEN word IN('phone', 'eyes', 'hair', 'body') THEN 1 ELSE 0 END) = 0
One way to do it is to use not exists, where the inner query is linked to the outer query by id and is filtered by the search words.
First, create and populate sample table (Please save us this step in your future questions):
DECLARE #T AS TABLE (
id int,
word varchar(20)
)
INSERT INTO #T VALUES
(1, 'hello'),
(2, 'friends'),
(2, 'world'),
(3, 'cat'),
(3, 'dog'),
(2, 'country'),
(1, 'phone'),
(4, 'eyes')
The query:
SELECT DISTINCT id
FROM #T t0
WHERE NOT EXISTS
(
SELECT 1
FROM #T t1
WHERE word IN('phone', 'eyes', 'hair', 'body')
AND t0.Id = t1.Id
)
Result:
id
2
3
SELECT t.id FROM dbo.table AS t
WHERE NOT EXISTS (SELECT 1 FROM dbo.table AS t2
INNER JOIN
(VALUES('phone'),('eyes'),('hair'),('body')) AS lw(word)
ON t2.word = lw.word
AND t2.id = t.id)
GROUP BY t.id;
You can try this as well: this is a dynamic table structure:
DECLARE #T AS TABLE (id int, word varchar(20))
INSERT INTO #T VALUES
(1, 'hello'),
(2, 'friends'),
(2, 'world'),
(3, 'cat'),
(3, 'dog'),
(2, 'country'),
(1, 'phone'),
(4, 'eyes')
DECLARE #tblNotUsed AS TABLE ( id int, word varchar(20))
DECLARE #tblNotUsedIds AS TABLE (id int)
INSERT INTO #tblNotUsed VALUES
(1, 'phone'),
(2, 'eyes'),
(3, 'hair'),
(4, 'body')
INSERT INTO #tblNotUsedIds (id)
SELECT [#T].id FROM #T INNER JOIN #tblNotUsed ON [#tblNotUsed].word = [#T].word
SELECT DISTINCT id FROM #T
WHERE id NOT IN (SELECT id FROM #tblNotUsedIds)
The nice thing about SQL is there are sometimes many ways to do things. Here is one way is to place your list of known values into a #temp table and then run something like this.
Select * from dbo.maintable
EXCEPT
Select * from #tempExcludeValues
The results will give you all records that aren't in your predefined list. A second way is to do the join like Larnu has mentioned in the comment above. NOT IN is typically not the fastest way to do things on larger datasets. JOINs are by far the most efficient method of filtering data. Many times better than using a IN or NOT IN clause.
I have T-SQL Table below.
ID Cost MaxCost
-------------------------------
2 200 300
3 400 1000
6 20 100
The above table must have 10 rows with IDs 1 to 10. So its missing 7 rows. How do i insert missing rows with proper ID. The cost & maxcost for missing rows will be zero. Do i need to create a temp table that holds 1 to 10 numbers?
No need for temp table, simple tally derived table and LEFT OUTER JOIN are sufficient:
CREATE TABLE #tab(ID INT, Cost INT, MaxCost INT);
INSERT INTO #tab(ID, Cost, MaxCost)
VALUES (2, 200,300),(3, 400, 1000) ,(6, 20, 100);
DECLARE #range_start INT = 1
,#range_end INT = 10;
;WITH tally AS
(
SELECT TOP 1000 r = ROW_NUMBER() OVER (ORDER BY name)
FROM master..spt_values
)
INSERT INTO #tab(id, Cost, MaxCost)
SELECT t.r, 0, 0
FROM tally t
LEFT JOIN #tab c
ON t.r = c.ID
WHERE t.r BETWEEN #range_start AND #range_end
AND c.ID IS NULL;
SELECT *
FROM #tab
ORDER BY ID;
LiveDemo
EDIT:
Tally table is simply number table. There are many ways to achieve it with subquery:
recursive cte
ROW_NUMBER() from system table that holds many values (used here)
UNION ALL and CROSS JOIN
VALUES(...)
using OPENJSON (SQL Server 2016+)
...
The TOP 1000 will generate only 1000 records if you know that you need more you can use:
SELECT TOP 1000000 r = ROW_NUMBER() OVER (ORDER BY (SELECT 1))
FROM master..spt_values c
CROSS JOIN master..spt_values c2;
Since your number of rows is low you could just define the data explicitly...
CREATE TABLE Data(ID INT, Cost INT, MaxCost INT);
INSERT INTO Data(ID, Cost, MaxCost) VALUES(2, 200, 300);
INSERT INTO Data(ID, Cost, MaxCost) VALUES(3, 400, 1000);
INSERT INTO Data(ID, Cost, MaxCost) VALUES(6, 20, 100);
and the query...
select *
from (VALUES(1),(2),(3),(4),(5),(6),(7),(8),(9),(10)) RowNums (ID)
left outer join Data on RowNums.ID = Data.ID
The first part defines a column ID with rows 1-10, it then left outer joins to your data. The beauty of this is that it is very readable.
I like to google for new and better ways to do things.. so i stumbled over this post and...Well what worked good in SQL7 and works good in SQL2016 is to just use a outer join and look for NULL values(null is missing data) ....
insert into DestTable (keyCol1,col1,col2,col3...)
select keyCol1,col1,col2,col3,...)
from SouceTable as s
left outer join DestTable as d on d.KeyCol1=s.KeyCol1
where d.KeyCol1 is null
and ...
feel free to test it
wrap your statement in a transaction, delete a few rows and see them come back in the select statement that would normally insert the rows in the destination table...
BEGIN TRAN
--> delete a subset of the data, in this case 5 rows
set rowcount 5;
-->delete and show what is deleted
delete from DestTable;
OUTPUT deleted.*,'DELETD' as [Action]
--> Perform the select to see if the selected rows that are retured match the deleted rows
--insert into DestTable (keyCol1,col1,col2,col3...)
Select keyCol1,col1,col2,col3,...)
from SouceTable as s
left outer join DestTable as d on d.KeyCol1=s.KeyCol1
where d.KeyCol1 is null
and ...
ROLLBACK
another way would be a TSQL merge, google that if you need to also update and optionally delete...
I have 3 tables:
recipe:
id, name
ingredient:
id, name
recipeingredient:
id, recipeId, ingredientId, quantity
Every time, a customer creates a new recipe, I need to check the recipeingredient table to verify if this recipe exists or not. If ingredientId and quantity are exactly the same, I will tell the customer the recipe already exists. Since I need to check multiple rows, need help to write this query.
Knowing your ingredients and quantities, you can do something like this:
select recipeId as ExistingRecipeID
from recipeingredient
where (ingredientId = 1 and quantity = 1)
or (ingredientId = 8 and quantity = 1)
or (ingredientId = 13 and quantity = 1)
group by recipeId
having count(*) = 3 --must match # of ingeredients in WHERE clause
I originally thought that the following query would find pairs of recipes that have exactly the same ingredients:
select ri1.recipeId, ri2.recipeId
from RecipeIngredient ri1 full outer join
RecipeIngredient ri2
on ri1.ingredientId = ri2.ingredientId and
ri1.quantity = ri2.quantity and
ri1.recipeId < ri2.recipeId
group by ri1.recipeId, ri2.recipeId
having count(ri1.id) = count(ri2.id) and -- same number of ingredients
count(ri1.id) = count(*) and -- all r1 ingredients are present
count(*) = count(ri2.id) -- all r2 ingredents are present
However, this query doesn't count things correctly, because the mismatches don't have the right pairs of ids. Alas.
The following does do the correct comparison. It counts the ingredients in each recipe before the join, so this value can just be compared on all matching rows.
select ri1.recipeId, ri2.recipeId
from (select ri.*, COUNT(*) over (partition by recipeid) as numingredients
from #RecipeIngredient ri
) ri1 full outer join
(select ri.*, COUNT(*) over (partition by recipeid) as numingredients
from #RecipeIngredient ri
) ri2
on ri1.ingredientId = ri2.ingredientId and
ri1.quantity = ri2.quantity and
ri1.recipeId < ri2.recipeId
group by ri1.recipeId, ri2.recipeId
having max(ri1.numingredients) = max(ri2.numingredients) and
max(ri1.numingredients) = count(*)
The having clause guarantees that each recipe that the same number of ingredients, and that the number of matching ingredients is the total. This time, I've tested it on the following data:
insert into #recipeingredient select 1, 1, 1
insert into #recipeingredient select 1, 2, 10
insert into #recipeingredient select 2, 1, 1
insert into #recipeingredient select 2, 2, 10
insert into #recipeingredient select 2, 3, 10
insert into #recipeingredient select 3, 1, 1
insert into #recipeingredient select 4, 1, 1
insert into #recipeingredient select 4, 3, 10
insert into #recipeingredient select 5, 1, 1
insert into #recipeingredient select 5, 2, 10
If you have a new recipe, you can modify this query to just look for the recipe in one of the tables (say ri1) using an additional condition on the on clause.
If you place the ingredients in a temporary table, you can substitute one of these tables, say ri1, with the new table.
You might try something like this to find if you have a duplicate:
-- Setup test data
declare #recipeingredient table (
id int not null primary key identity
, recipeId int not null
, ingredientId int not null
, quantity int not null
)
insert into #recipeingredient select 1, 1, 1
insert into #recipeingredient select 1, 2, 10
insert into #recipeingredient select 2, 1, 1
insert into #recipeingredient select 2, 2, 10
-- Actual Query
if exists (
select *
from #recipeingredient old
full outer join #recipeingredient new
on old.recipeId != new.recipeId -- Different recipes
and old.ingredientId = new.ingredientId -- but same ingredients
and old.quantity = new.quantity -- and same quantities
where old.id is null -- Match not found
or new.id is null -- Match not found
)
begin
select cast(0 as bit) as IsDuplicateRecipe
end
else begin
select cast(1 as bit) as IsDuplicateRecipe
end
Since this is really only searching for a duplicate, you might want to substitute a temp table or pass a table variable for the "new" table. This way you wouldn't have to insert the new records before doing your search. You could also insert into the base tables, wrap the whole thing in a transaction and rollback based upon the results.
I have a problem that I would like have solved via a SQL query. This is going to
be used as a PoC (proof of concept).
The problem:
Product offerings are made up of one or many product instances, a product
instance can belong to many product offerings.
This can be realised like this in a table:
PO | PI
-----
A | 10
A | 11
A | 12
B | 10
B | 11
C | 13
Now I would like to get back the product offer from a set of product instances.
E.g. if we send in 10,11,13 the expected result back is B & C, and if we send in
only 10 then the result should be NULL since no product offering is made up of
only 10. Sending in 10,11,12 would result in A (not A & B since 12 is not a valid product offer in it self).
Prerequisites:
The combination of product instances sent in can only result in one specific
combination of product offerings, so there is only one solution to each query.
Okay, I think I have it. This meets the constraints you provided. There might be a way to simplify this further, but it ate my brain a little:
select distinct PO
from POPI x
where
PO not in (
select PO
from POPI
where PI not in (10,11,12)
)
and PI not in (
select PI
from POPI
where PO != x.PO
and PO not in (
select PO
from POPI
where PI not in (10,11,12)
)
);
This yields only results who fill the given set which are disjoint with all other results, which I think is what you were asking for. For the test examples given:
Providing 10,11,12 yields A
Providing 10,11,13 yields B,C
Edit: Whilst I think mine works fine, Adam's answer is without a doubt more elegant and more efficient - I'll just leave mine here for posterity!
Apologies since I know this has been tagged as an Oracle issue since I started playing. This is some SQL2008 code which I think works for all the stated cases....
declare #test table
(
[PI] int
)
insert #test values (10), (11), (13)
declare #testCount int
select #testCount = COUNT(*) from #test
;with PO_WITH_COUNTS as
(
select PO_FULL.PO, COUNT(PO_FULL.[PI]) PI_Count
from ProductOffering PO_FULL
left
join (
select PO_QUALIFYING.PO, PO_QUALIFYING.[PI]
from ProductOffering PO_QUALIFYING
where PO_QUALIFYING.[PI] in (select [PI] from #test)
) AS QUALIFYING
on QUALIFYING.PO = PO_FULL.PO
and QUALIFYING.[PI] = PO_FULL.[PI]
group by
PO_FULL.PO
having COUNT(PO_FULL.[PI]) = COUNT(QUALIFYING.[PI])
)
select PO_OUTER.PO
from PO_WITH_COUNTS PO_OUTER
cross
join PO_WITH_COUNTS PO_INNER
where PO_OUTER.PI_Count = #testCount
or PO_OUTER.PO <> PO_INNER.PO
group by
PO_OUTER.PO, PO_OUTER.PI_Count
having PO_OUTER.PI_Count = #testCount
or PO_OUTER.PI_Count + SUM(PO_INNER.PI_Count) = #testCount
Not sure if Oracle has CTEs but could just state the inner query as two derived tables. The cross join in the outer query lets us find combinations of offerings that have all the valid items. I know that this will only work based on the statement in the question that the data is such that there is only 1 valid combination for each requested set, Without that it's even more complicated as counts are not enough to remove combinations that have duplicate products in them.
I don't have a db in front of me, but off the top of my head you want the list of POs that don't have any PIs not in your input list, ie
select distinct po
from tbl
where po not in ( select po from tbl where pi not in (10,11,13) )
Edit: Here are the example other cases:
When input PI = 10,11,13 the inner select returns A so the outer select returns B, C
When input PI = 10 the inner select returns A,B,C so the outer select returns no rows
When input PI = 10,11,12 the inner select returns C so the outer select returns A,B
Edit: Adam has pointed out that this last case doesn't meet the requirement of only returning A (that'll teach me for rushing), so this isn't yet working code.
Select Distinct PO
From Table T
-- Next eliminates POs that contain other PIs
Where Not Exists
(Select * From Table
Where PO = T.PO
And PI Not In (10, 11, 12))
-- And this eliminates POs that do not contain all the PIs
And Not Exists
(Select Distinct PI From Table
Where PI In (10, 11, 12)
Except
Select Distinct PI From Table
Where PO = T.PO
or, if your database does not implement EXCEPT...
Select Distinct PO
From Table T
-- Next predicate eliminates POs that contain other PIs
Where Not Exists
(Select * From Table
Where PO = T.PO
And PI Not In (10, 11, 12))
-- And this eliminates POs that do not contain ALL the PIs
And Not Exists
(Select Distinct PI From Table A
Where PI In (10, 11, 12)
And Not Exists
(Select Distinct PI From Table
Where PO = T.PO
And PdI = A.PI))
Is it possible that a customers asks for a product more than once?
For example: he/she asks an offering for 10,10,11,11,12?
If this is possible than solutions like
select ...
from ...
where pi in (10,10,11,11,12)
will not work.
Because 'pi in (10,10,11,11,12)' is the same as 'pi in (10,11,12)'.
A solution for 10,10,11,11,12 is A&B.
well some pseudo code from the top of my head here:
select from table where PI = 10 or pi =11, etc
store the result in a temp table
select distinct PO and count(PI) from temp table.
now for each PO you can get the total available PI offerings. if the number of PIs available matches the count in the temp table, it means that you have all the PIs for that PO. add all the POs and you ave your result set.
You will need a count of the items in your list, i.e. #list_count. Figure out which Offerings have Instances that aren't in the list. Select all Offerings that aren't in that list and do have Instances in the list:
select P0,count(*) c from table where P0 not in (
select P0 from table where P1 not in (#list)
) and P1 in (#list) group by P0
I would store that in a temp table and select * records where c = #list_count
If we redefine a bit a problem:
Lets have a customer table with product instances:
crete table cust_pi (
pi varchar(5),
customer varchar(5));
And a "product_catalogue" table:
CREATE TABLE PI_PO_TEST
("PO" VARCHAR2(5 CHAR),
"PI" VARCHAR2(5 CHAR)
);
Lets fill it with some sample data:
insert into CUST_PI (PI, CUSTOMER)
values ('11', '1');
insert into CUST_PI (PI, CUSTOMER)
values ('10', '1');
insert into CUST_PI (PI, CUSTOMER)
values ('12', '1');
insert into CUST_PI (PI, CUSTOMER)
values ('13', '1');
insert into CUST_PI (PI, CUSTOMER)
values ('14', '1');
insert into PI_PO_TEST (PO, PI)
values ('A', '10');
insert into PI_PO_TEST (PO, PI)
values ('A', '11');
insert into PI_PO_TEST (PO, PI)
values ('A', '12');
insert into PI_PO_TEST (PO, PI)
values ('A', '13');
insert into PI_PO_TEST (PO, PI)
values ('B', '14');
insert into PI_PO_TEST (PO, PI)
values ('C', '11');
insert into PI_PO_TEST (PO, PI)
values ('C', '12');
insert into PI_PO_TEST (PO, PI)
values ('D', '15');
insert into PI_PO_TEST (PO, PI)
values ('D', '14');
Then my first shoot solution is like this:
select po1 po /* select all product offerings that match the product definition
(i.e. have the same number of product instances per offering as
in product catalogue */
from (select po po1, count(c.pi) k1
from cust_pi c, pi_po_test t
where c.pi = t.pi
and customer = 1
group by po) t1,
(select po po2, count(*) k2 from pi_po_test group by po) t2
where k1 = k2
and po1 = po2
minus /* add those, that are contained within others */
select slave
from (select po2 master, po1 slave
/* this query returns, that if you have po "master" slave should be removed from result,
as it is contained within*/
from (select t1.po po1, t2.po po2, count(t1.po) k1
from pi_po_test t1, pi_po_test t2
where t1.pi = t2.pi
group by t1.po, t2.po) t1,
(select po, count(po) k2 from pi_po_test group by po) t2
where t1.po2 = t2.po
and k1 < k2)
where master in
/* repeated query from begining. This could be done better :-) */
(select po1 po
from (select po po1, count(c.pi) k1
from cust_pi c, pi_po_test t
where c.pi = t.pi
and customer = 1
group by po) t1,
(select po po2, count(*) k2 from pi_po_test group by po) t2
where k1 = k2
and po1 = po2)
All of that was done on Oracle, so your mileage may vary
I tested this under 4 sets of values and they all returned a correct result. This uses a function that I use in SQL to generate a table from a string of parameters separated by semicolons.
DECLARE #tbl TABLE (
po varchar(10),
pii int)
INSERT INTO #tbl
SELECT 'A', 10
UNION ALL
SELECT 'A', 11
UNION ALL
SELECT 'A', 12
UNION ALL
SELECT 'B', 10
UNION ALL
SELECT 'B', 11
UNION ALL
SELECT 'C', 13
DECLARE #value varchar(100)
SET #value = '10;11;12;'
--SET #value = '11;10;'
--SET #value = '13;'
--SET #value = '10;'
SELECT DISTINCT po
FROM #tbl a
INNER JOIN fMultiValParam (#value) p ON
a.pii = p.paramid
WHERE a.po NOT IN (
SELECT t.po
FROM #tbl t
LEFT OUTER JOIN (SELECT *
FROM #tbl tt
INNER JOIN fMultiValParam (#value) p ON
tt.pii = p.paramid) tt ON
t.pii = tt.pii
AND t.po = tt.po
WHERE tt.po IS NULL)
here's the function
CREATE FUNCTION [dbo].[fMultiValParam]
(#Param varchar(5000))
RETURNS #tblParam TABLE (ParamID varchar(40))
AS
BEGIN
IF (#Param IS NULL OR LEN(#Param) < 2)
BEGIN
RETURN
END
DECLARE #len INT
DECLARE #index INT
DECLARE #nextindex INT
SET #len = DATALENGTH(#Param)
SET #index = 0
SET #nextindex = 0
WHILE (#index < #len)
BEGIN
SET #Nextindex = CHARINDEX(';', #Param, #index)
INSERT INTO #tblParam
SELECT SUBSTRING(#Param, #index, #nextindex - #index)
SET #index = #nextindex + 1
END
RETURN
END
Try this:
SELECT DISTINCT COALESCE ( offer, NULL )
FROM products
WHERE instance IN ( #instancelist )
IMHO impossible via pure SQL without some stored-procedure code. But... i'm not sure.
Added: On the other hand, I'm getting an idea about a recursive query (in MSSQL 2005 there is such a thing, which allows you to join a query with it's own results until there are no more rows returned) which might "gather" the correct answers by cross-joining the results of previous step with all products and then filtering out invalid combinations. You would however get all permutations of valid combinations and it would hardly be efficient. And the idea is pretty vague, so I can't guarantee that it can actually be implemented.