Return master rows based on detail filter - sql

I have a query where I want to return Master rows based on whether the detail fulfil a certain criteria.
For example, I only want to return a particular Master row if AT LEAST one of the Detail rows have SomeProperty = X.
Based on the following predicate:
predicate = predicate.And(p =>
p.BasketItems.Where(obi => obi.BasketItemTypeID ==
(int) BasketType.Refund).Count() > 0);
generates the following SQL:
SELECT COUNT(*)
FROM [dbo].[BasketItems] AS [t3]
WHERE ([t3].[BasketId] = [t0].[OrderBasketID]) AND ([t3].[BasketItemTypeID] = 3)
)) > 0)
Problem with this is it's doing a table scan, so the query takes a while to run.
Just checking that I'm not doing anything crazy and wonder if there's anything that can speed up this query?
Thanks
Duncan

select M.basketID, max(M.field1) as field1, max(M.field2) as field2
from dbo.basketItems as M
Inner join detail on M.basketID = detail.basketID
where detail.basketItemTypeID = '3'
group by M.basketID
(Join master to detail. select all rows where detail has the required criterion. Squish the resulting rows down to 1 per master record.)

Related

How to retrieve data based on multiple conditions from multiple rows from two joined tables?

[Table 1]
[Table 2]
[select *]
SELECT
product.xid as xid,
product.option_group_id as option_group_id,
product.brand_id as brand_id
FROM product_promotion as product
LEFT JOIN product_promotion_to_filters as filters
ON(filters.product_xid = product.xid)
WHERE (filters.options_xid = '04320095' and filters.value_xid = '50073608') and (filters.options_xid = '85047331' and filters.value_xid = '77933356')
No value is returned. empty query
My problem with the where part. Value does not match because multiple rows are returned
The result I want in the condition section is to only get the matching records from multiple preferences.
WHERE (filters.options_xid = '04320095' and filters.value_xid = '50073608') and (filters.options_xid = '85047331' and filters.value_xid = '77933356')
As a result of this query, I want only the data with xid 27145569 to come
Since there is no data in one row, I can only make one condition, but sometimes 2-3 or more conditions need to be matched.
This is how I found the solution.
WHERE CONCAT(filters.options_xid,filters.value_xid) IN ('0432009550073608', '8504733163299671') HAVING COUNT(product.xid) >= 2;
You can use IN command to filter the result in WHERE clause.
Let me know if it works.

Update twice nested repeated record

I'm struggling with this query (dummy version, there are much more fields in it) :
UPDATE
table1 as base
SET
lines =
ARRAY(
SELECT AS STRUCT
b.line_id,
s.purch_id,
ARRAY(
SELECT AS STRUCT
wh.warehouse_id,
s.is_proposed,
FROM table1 as t, UNNEST(lines) as lb, UNNEST(lb.warehouses) as wh
INNER JOIN
(SELECT
l.line_id,
wh.is_proposed
FROM table2, UNNEST(lines) as l, UNNEST(l.warehouses) as wh) as s
ON lb.line_id = s.line_id AND wh.warehouse_id = s.warehouse_id)
FROM table1, UNNEST(lines) as b
INNER JOIN UNNEST(supply.lines) as s
ON b.line_id = s.line_id)
FROM
table2 as supply
WHERE
base.date = supply.date
AND
base.sales_id = supply.sales_id
table1 and table2 have the same nesting :
lines : repeated record
lines.warehouses : repeated record within lines
(so {... , lines [{... warehouses [)
Plus table1 is a subset of table2 with a subset of its fields, table1 having them NULL from start (I refresh information when data is available because informations are asynchroneous).
I first tried this as a first step (which succeeds) :
UPDATE
table1 as base
SET
lines =
ARRAY(
SELECT AS STRUCT
b.line_id,
s.purch_id,
b.warehouses
FROM table1, UNNEST(lines) as b
INNER JOIN UNNEST(supply.lines) as s
ON b.line_id = s.line_id)
FROM
table2 as supply
WHERE
base.date = supply.date
AND
base.sales_id = supply.sales_id
But the fact I actually need to update lines.warehouses too so I'm happy it works but it is not enough.
The full query is valid and when I try the last ARRAY part in a terminal, the query is fast and the output has no duplicate.
Still the complete UPDATE does not end (after 20 min, I killed it).
Tables are not that large, 20k from both side (220k completely flattened).
So am I doing something wrong ?
Is there a better way ?
Thanks
I finally solved the issue, Iit was way simpler than I thought.
I think I misunderstood how the whole query nesting worked.
So I just linked every data avaiable, from the first singular row matched to the last array since filtered data at top level is propagated to lower levels.
UPDATE
table1 as base
SET
lines =
ARRAY(
SELECT AS STRUCT
b.line_id,
s.purch_id,
ARRAY(
SELECT AS STRUCT
wh.warehouse_id,
sh.is_proposed,
FROM UNNEST(b.warehouses) as wh -- take only upper level data
INNER JOIN UNNEST(s.warehouses) as sh -- idem
ON wh.warehouse_id = sh.warehouse_id) -- no need to 'redo' the joining on already filtered ones
FROM UNNEST(base.lines) as b
INNER JOIN UNNEST(supply.lines) as s
ON b.line_id = s.line_id)
FROM
table2 as supply
WHERE
base.date = supply.date
AND
base.sales_id = supply.sales_id
The query succeeds in less than 1min

postgres - How to update the same record twice in a joined update query

I'm trying to write the following migration that drops the conversations_users table (which was a join table that included a read_up_to column) and copies the read_up_to information into the new messages.read_by_user_ids array. For every 1 message row there are at least 2 conversations_users rows, so this join is repeating messages. I expected the following expression to work, but it's only assigning one user_id to the read_by_user_ids array, and I'm guessing that's because the update isn't happening sequentially.
Result:
message_id: 1, read_by_user_ids: { 15 }
Desired result: message_id: 1, read_by_user_ids: { 15, 19 }
UPDATE
messages as m
SET
read_by_user_ids = CASE
WHEN cu.read_up_to >= m.created_at THEN array_append(
COALESCE(m.read_by_user_ids, '{}'),
cu.user_id
) ELSE m.read_by_user_ids
END
FROM
conversations_users cu
WHERE
cu.conversation_id = m.thread_id;
I'm on my phone, so apologies for untested typos.
As per my comment, aggregate the individual incoming user_ids into one array per conversation. Then use array_cat to combine the two arrays.
This way you only need to do one update per target row.
I also noticed that you only want to update rows based on a date comparison, so I added that to the sub query I proposed.
UPDATE
messages as m
SET
read_by_user_ids = array_cat(
COALESCE(m.read_by_user_ids, '{}'),
cu.user_id_array
)
FROM
(
SELECT
cu.conversation_id,
array_agg(cu.user_id) AS user_id_array
FROM
messages
INNER JOIN
conversations_users
ON cu.conversation_id = m.thread_id
AND cu.read_up_to >= m.created_at
GROUP BY
cu.conversation_id
)
cu
WHERE
cu.conversation_id = m.thread_id;
There are many other options on how to generate the array in the sub-query. Which is the most efficient will depend on the profile of your data, indexes, etc. But the principle remains the same; updating the same row multiple times in a single statement doesn't work, you need to update each row once, with an array as the input.

Different results on the basis of different foreign key value using a falg in where clause

Please see attached image.
alt text http://img241.imageshack.us/img241/3585/customcost.png
Can you please tell me what query will work. Please ignore isdefinite and productpriceid columns.
Thanks
If you want a single query, this should do it if I've interpreted your question properly:
SELECT DISTINCT t1.SupplierVenueProductID, [...]
FROM table t1
LEFT JOIN
table t2
ON t1.SupplierVenueProductID = t2.SupplierVenueProductID
AND t2.iscustomcost = 1
WHERE t2.SupplierVenueProductID IS NULL
OR t1.iscustomcost = 1
I don't know your table name, but you join it to itself.
I'm a bit lost on what you want to accomplish here,
going by your requirement if isCustomCost = 1 then return record #3 from SupplierVenueProductId 1 and both records from SupplierVenueProductId 2
Trying to generalize this, I think what you need is :
return all rows from the table, unless when there is a record for a SupplierVenueProductId that has isCustomCost = 1, then only return that record for this SupplierVenueProductId
Which then becomes something along the lines of :
SELECT t1.*
FROM myTable t1
WHERE t1.isCustomCost = 1
OR NOT EXISTs (SELECT *
FROM t2
WHERE t2.SupplierVenueProductId = t1.SupplierVenueProductId
AND t2.isCustomCost = 1)
Hope this helps.

outer query to list only if its rowcount equates to inner subquery

Need help on a query using sql server 2005
I am having two tables
code
chargecode
chargeid
orgid
entry
chargeid
itemNo
rate
I need to list all the chargeids in entry table if it contains multiple entries having different chargeids
which got listed in code table having the same charge code.
data :
code
100,1,100
100,2,100
100,3,100
101,11,100
101,12,100
entry
1,x1,1
1,x2,2
2,x3,2
11,x4,1
11,x5,1
using the above data , it query should list chargeids 1 and 2 and not 11.
I got the way to know how many rows in entry satisfies the criteria, but m failing to get the chargeids
select count (distinct chargeId)
from entry where chargeid in (select chargeid from code where chargecode = (SELECT A.chargecode
from code as A join code as B
ON A.chargecode = B.chargeCode and A.chargetype = B.chargetype and A.orgId = B.orgId AND A.CHARGEID = b.CHARGEid
group by A.chargecode,A.orgid
having count(A.chargecode) > 1)
)
First off: I apologise for my completely inaccurate original answer.
The solution to your problem is a self-join. Self-joins are used when you want to select more than one row from the same table. In our case we want to select two charge IDs that have the same charge code:
SELECT DISTINCT c1.chargeid, c2.chargeid FROM code c1
JOIN code c2 ON c1.chargeid != c2.chargeid AND c1.chargecode = c2.chargecode
JOIN entry e1 ON e1.chargeid = c1.chargeid
JOIN entry e2 ON e2.chargeid = c2.chargeid
WHERE c1.chargeid < c2.chargeid
Explanation of this:
First we pick any two charge IDs from 'code'. The DISTINCT avoids duplicates. We make sure they're two different IDs and that they map to the same chargecode.
Then we join on 'entry' (twice) to make sure they both appear in the entry table.
This approach gives (for your example) the pairs (1,2) and (2,1). So we also insist on an ordering; this cuts to result set down to just (1,2), as you described.