Set column value when multiple rows exist - sql

I have a flattened data set that includes Order#, Shipment#, and ShippingCharges. There can be multiple shipments per order, but shipping charges are collected at the order level. Here is an example dataset:
1, 1, $5.00
2, 1, $6.00
2, 2, $6.00
3, 1, $10.00
3, 2, $10.00
3, 3, $10.00
4, 1, $4.00
As you can see, the order's ShippingCharges are repeated for each shipment in the data set. I need to come up with a query that will set ShippingCharges to 0 when there are multiple shipments on the order. The resulting dataset would look like this:
1, 1, $5.00
2, 1, $6.00
2, 2, $0.00
3, 1, $10.00
3, 2, $0.00
3, 3, $0.00
4, 1, $4.00
It is important to note that the Shipment# numbers do not all reset to 1 for each order. I did this in the sample dataset to make it easier to follow. Shipment# is actually a sequential integer that increments each time a shipment is created, so a simple UPDATE dataset SET ShippingCharges=0 WHERE Shipment# > 1 is NOT the answer.
It seems like I would need to do an UPDATE when there is more than 1 shipment for an order, but only for rows where the Shipment# is greater than the minimum Shipment# for the order.
Any ideas what that query might look like, especially for Microsoft Access?

UPDATE dataset SET ShippingCharges=0 WHERE Shipment# > 1 is NOT
the answer.
Then set the charge to zero when Shipment# does not match the minimum Shipment# for that Order#.
UPDATE dataset
SET ShippingCharges=0
WHERE [Shipment#] <> DMin("[Shipment#]", "dataset", "[Order#]=" & [Order#])
If the Order# field is text datatype, add quotes in the third DMin argument (Criteria):
DMin("[Shipment#]", "dataset", "[Order#]='" & [Order#] & "'")

This was written in Oracle, so not sure if you can do similiar in Access.
Sub select to the table to see if it's the min shipment or not then you use the shipment charge or 0 it out.
select a.order_num, a.shipment_num,
case when a.shipment_num = (
select min(b.shipment_num)
from order_table b
where b.order_num = a.order_num
) then max(a.shipment_charges) else '0' end
from order_table a
group by order_num, shipment_num
order by order_num, shipment_num

Related

Something like GROUP BY HAVING ALL IN [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed last month.
Improve this question
I want to select a ProductConfig that has exactly the given variants. As it is a many-to-many relationship I have an association table. With the association table I have been trying to use it with the GROUP BY so I can work on the other column.
The problem I am having is that I need an exactly equal operator to a set of values inside the HAVING. Something like HAVING variant_id = (1, 2, 3, 99).
For now I have the following query with some problems
SELECT productconfig_id
FROM association_productconfig_elementvariant
GROUP BY productconfig_id
HAVING variant_id IN (1, 2, 3, 99);
This will match if productconfig_id has variant_id equal to ANY subset of {1, 2 3, 99} like {1, 2} or {1, 3} but I only want it to match with the exact set {1, 2, 3, 99}.
I have another problem the other way around. If productconfig_id has variant_id equal to {1, 2, 50} it will also match because the first to is in the values even though the last is not.
Basically I want to compare equality over a column and a set of values. This second problem will solve if you had something like HAVING ALL IN.
This is probably more on-target with what you need. Here, I am doing both a COUNT() and a sum() based on the matching variant_id in question. This is making sure that whatever records DO qualify the variant get to the 4 count, but ALSO the count() of every variant per configuration.
So, if one product had variants of (1, 2, 3, 5, 12, 99, 102, 150) would have a count(*) = 8, but the specific match = 4 based on those in question.
Now, if you can ignore the overall count of 8, just remove that AND portion from below, but at least you know the primary 4 in consideration are accounted for.
SELECT
productconfig_id
FROM
association_productconfig_elementvariant
GROUP BY
productconfig_id
HAVING
sum( case when variant_id in ( 1, 2, 3, 99 )
then 1 else 0 end ) = 4
AND count(*) = 4
Could you try this :
Select productconfig_id
From (
SELECT productconfig_id, count(1) as _count
FROM association_productconfig_elementvariant
GROUP BY productconfig_id
HAVING variant_id IN (1, 2, 3, 99)
) as s where _count = 4;
Basicly the only productconfig with count = 4 will be the one you are looking for.

Expanding MS-Access graph of 1 aggregate sum value to up to 5 aggregate sum values selected by a user

I have a working form which creates an access graph report based on a value selected by the user in an access form - the SQL to create the graph data is:
SELECT AccountBalances.RecDate,
Sum(AccountBalances.[End Balance]) AS [SumOfEnd Balance],
CombinedFSLineItems.[CFS LineDescription]
FROM (CombinedFSLineItems
INNER JOIN AccountNumbers
ON CombinedFSLineItems.[CFS LineItem] = AccountNumbers.[CFS LineItem])
INNER JOIN AccountBalances
ON AccountNumbers.[Account#] = AccountBalances.[Account#]
GROUP BY AccountBalances.RecDate,
CombinedFSLineItems.[CFS LineDescription],
AccountBalances.StoreID, CombinedFSLineItems.[CFS LineItem]
HAVING (((AccountBalances.RecDate) >= [TempVars]![varStartDate]
AND (AccountBalances.RecDate) <= [TempVars]![varDate])
AND ((AccountBalances.StoreID) = [TempVars]![varStoreID])
AND ((CombinedFSLineItems.[CFS LineItem]) = [TempVars]![varCFSLineItem]));
A little background would help here. There are 3 tables used by the query
CombinedFSLineItems: A report Definition Table which defines the line Items of a financial report. Relevant fields for this discussion are [CFS LineItem] and [CFS LineDescription].
AccountNumbers: A table that lists all the account numbers and attributes such as which CFS Report LineItem it is part of and which Store it is associated with. Relevant fields for this discussion are [StoreID], [CFS LineItem] and [Account#].
AccountBalances: A table that contains the monthly data for every account for each store. The relevant fields for this discussion are [StoreID], [Account#], and [End Balance].
The user selects the store and CFS LineItem to be graphed on a Form which sets the TempVars: [varStoreID] and [varCFSLineItem]. The query is the source of the graph report data. It sums all the accounts that comprise that CFS Line Item for that store and then the graph presents the data over the months available in the data.
Now I'd like to allow the user to select up to 5 Line Items and create a graph of all 5. Any and all ideas on best way to do that would be appreciated.
Thanks!
Mike
EDIT 4/13/20 1:30pm
Sample Data...
Combined FSLineItems Tbl
CFS, LineItemCFS, Line, Description
1, Actual Cash
2, Contracts in Transit
7, Customer COD
19, Fixed Assets
…
AccountNumbers Tbl
StoreID, Account#, CFS LineItem
1, 101, 1
1, 102, 1
1, 103, 1
1, 104, 2
1, 105, 2
1, 106, 19
1, 107, 19
1, 108, 19
2, 101, 7
2, 102, 1
2, 106, 1
AccountBalances Tbl
Account#, StoreID, End Balance, RecDate
101, 1, 10,000.00, 1/1/2020
101, 1, 15,000.00, 2/1/2020
101, 1, 5,000.00, 12/1/2019
102, 1, 1,000.00, 1/1/2020
102, 1, 500.00, 2/1/2020
102, 1, 1,000.00, 12/1/2019
104, 1, 2,500.00, 1/1/2020
104, 1, 3,000.00, 2/1/2020
104, 1, 1,500.00, 12/1/2019
105, 1, 1,000.00, 1/1/2020
105, 1, 1,500.00, 2/1/2020
105, 1, 1,000.00, 12/1/2019
104, 2, 3,000.00, 1/1/2020
104, 2, 4,000.00, 12/1/2019
104, 2, 5,000.00, 2/1/2020
105, 2, 1,500.00, 1/1/2020
***
Using the above sample data, if the user selected store 1 and CFS LineItem 1 and CFS LineItem 2 I would want a graph with 2 Lines:
Line 1 being CFS LineItem1 having the values of 6,000 11,000 15,500 for Dec Jan Feb
Line 2 being CFS LineItem2 having the values of 2,500 3,500 4,500 for Dec Jan Feb
I'd also like to name the Lines in the graph based on the Combined FS LineItems Table field [CFS LineDescription].
Ideally, I could create a SQL statement that could be used as the source data for an Access Report Graph. If VBA code or macros are required that's fine.
Thanks!
Because MS Access charts usually follow Excel pivot table data model, you would simply need a longer query with same structure (i.e., more rows) which can be handled with an IN clause of more TempVars.
Consider following adjustment with varCFSLineItem1-5 variables. Also, below query converts HAVING to WHERE and uses table aliases for readability and repeats same columns in GROUP BY in SELECT. Be sure to adjust graph to needed fields:
SELECT b.RecDate,
b.StoreID,
c.[CFS LineDescription],
c.[CFS LineItem],
SUM(b.[End Balance]) AS [SumOfEnd Balance]
FROM (CombinedFSLineItems c
INNER JOIN AccountNumbers a
ON c.[CFS LineItem] = a.[CFS LineItem])
INNER JOIN AccountBalances b
ON a.[Account#] = b.[Account#]
WHERE (b.RecDate >= [TempVars]![varStartDate]
AND b.RecDate <= [TempVars]![varDate])
AND (b.StoreID = [TempVars]![varStoreID])
AND (c.[CFS LineItem] IN ([TempVars]![varCFSLineItem1],
[TempVars]![varCFSLineItem2],
[TempVars]![varCFSLineItem3],
[TempVars]![varCFSLineItem4],
[TempVars]![varCFSLineItem5])
)
GROUP BY b.RecDate,
c.[CFS LineDescription],
b.StoreID,
c.[CFS LineItem]

Can SQL Server perform an update on rows with a set operation on the aggregate max or min value?

I am a fairly experienced SQL Server developer but this problem has me REALLY stumped.
I have a FUNCTION. The function is referencing a table that is something like this...
PERFORMANCE_ID, JUDGE_ID, JUDGING_CRITERIA, SCORE
--------------------------------------------------
101, 1, 'JUMP_HEIGHT', 8
101, 1, 'DEXTERITY', 7
101, 1, 'SYNCHRONIZATION', 6
101, 1, 'SPEED', 9
101, 2, 'JUMP_HEIGHT', 6
101, 2, 'DEXTERITY', 5
101, 2, 'SYNCHRONIZATION', 8
101, 2, 'SPEED', 9
101, 3, 'JUMP_HEIGHT', 9
101, 3, 'DEXTERITY', 6
101, 3, 'SYNCHRONIZATION', 7
101, 3, 'SPEED', 8
101, 4, 'JUMP_HEIGHT', 7
101, 4, 'DEXTERITY', 6
101, 4, 'SYNCHRONIZATION', 5
101, 4, 'SPEED', 8
In this example there are 4 judges (with IDs 1, 2, 3, and 4) judging a performance (101) against 4 different criteria (JUMP_HEIGHT, DEXTERITY, SYNCHRONIZATION, SPEED).
(Please keep in mind that in my real data there are 10+ criteria and at least 6 judges.)
I want to aggregate the results in a score BY JUDGING_CRITERIA and then aggregate those into a final score by summing...something like this...
SELECT SUM (Avgs) FROM
(SELECT AVG(SCORE) Avgs
FROM PERFORMANCE_SCORES
WHERE PERFORMANCE_ID=101
GROUP BY JUDGING_CRITERIA) result
BUT... that is not quite what I want IN THAT I want to EXCLUDE from the AVG the highest and lowest values for each JUDGING_CRITERIA grouping. That is the part that I can't figure out. The AVG should be applied only to the MIDDLE values of the GROUPING FOR EACH JUDGING_CRITERIA. The HI value and the LO value for JUMP_HEIGHT should not be included in the average. The HI value and the LO value for DEXTERITY should not be included in the average. ETC.
I know this could be accomplished with a cursor to set the hi and lo for each criteria to NULL. But this is a FUNCTION and should be extremely fast.
I am wondering if there is a way to do this as a SET operation but still automatically exclude HI and LO from the aggregation?
Thanks for your help. I have a feeling it can probably be done with some advanced SQL syntax but I don't know it.
One last thing. This example is actually a simplification of the problem I am trying to solve. I have other constraints not mentioned here for the sake of simplicity.
Seth
EDIT: -Moved the WHERE clause to inside the CTE.
-Removed JudgeID from the partition
This would be my approach
;WITH Agg1 AS
(
SELECT PERFORMANCE_ID
,JUDGE_ID
,JUDGING_CRITERIA
,SCORE
,MinFind = ROW_NUMBER() OVER ( PARTITION BY PERFORMANCE_ID
,JUDGING_CRITERIA
ORDER BY SCORE ASC )
,MaxFind = ROW_NUMBER() OVER ( PARTITION BY PERFORMANCE_ID
,JUDGING_CRITERIA
ORDER BY SCORE DESC )
FROM PERFORMANCE_SCORES
WHERE PERFORMANCE_ID=101
)
SELECT AVG(Score)
FROM Agg1
WHERE MinFind > 1
AND MaxFind > 1
GROUP BY JUDGING_CRITERIA

Finding matching parents where all children also match

I'm trying to write a sql query in MS SQL Server 2008 that will match parent rows where the parents match and all their children match.
Assuming I have this basic table structure:
ParentTable:
ParentID, Item, Price
ChildTable:
ChildID, ParentID, Accessory, Price
I want to get a grouping of ParentIDs where the parents match on Item and Price and they have the same number of children, each of which match on Accessory and Price.
For example:
ParentTable:
---------------------
1, "Computer", 1000
2, "Stereo", 500
3, "Computer", 500
4, "Computer", 1000
ChildTable:
---------------------
1, 1, "Mouse", 10
2, 1, "Keyboard", 10
3, 2, "Speakers", 50
4, 3, "Keyboard", 10
4, 3, "Mouse", 10
5, 4, "Keyboard", 10
6, 4, "Mouse", 10
The expected results would be something like
ParentID, Grouping
---------------------
1, 1
2, 2
3, 3
4, 1
This would imply that ParentID 1 and 4 are exactly the same and 2 and 3 are unique. I dont really care about the format of the result, as long as I get a list of parents that match.
I'm not opposed to doing (some or all of) this in .net either.
your question is a little ambiguous, but I thought I'd give it a shot anyway.
here goes. Free form SQL. Hard to get it exactly right without access to some DML.
So this would be my general approach. This should work in SQL Server, probably Oracle as well. I'm not claiming this is perfect. My mental schema doesn't match above exactly, I'll leave that as an exercise for the reader. I typed it straight in.
SELECT DISTINCT p.id,p.name,p.dte,q.cnt
FROM parent p
JOIN
(
select p.id, p.dte, count(*) cnt
from parent p
join child ch
on ch.pid = p.id
group by p.id, p.dte
) q
ON p.id=q.id and p.dte=q.dte
GROUP BY p.id,p.name,q.cnt
ORDER BY p.id,p.name,q.cnt
btw: your question is a little ambiguous.
UPDATE:
this function looks promissing for the child rows to csv direction
http://sql-ution.com/function-to-convert-rows-to-csv/
OK if you can do this with temp tables, then this may give you ideas. Off the top of my head, so syntax not checked. Also, this is limited, as bigint typically only goes up to 2**63 or something.
First put unique child accessory and price into #child
create table #child ( accessory varchar, price decimal, id identity,
bnumber bigint null)
insert into #child(accessory, price)
select accesory,price from childtable group by accessory price
assuming id will be 1,2,3,4 etc
update #child set bnumber = 2**(id-1)
sets bnumber to 1,2,4,8 etc (this is where the bigint limitation may kick in). So now you have
mouse, 10,1,1
keyboard,10,2,2
speakers,50,3,4
Now you can sum these numbers by parent
select p.item, sum(ctemp.bnumber)
from parent p, child c, #child ctemp
where p.parentid = c.parentid
and c.accessory = ctemp.accessory
and c.price = ctemp.price
group by p.item
giving
1, 3
2, 4
3, 3
4, 3
..which I think is the answer you want. This is a bit clunky, and it's been a long day(!), but it might help.

SQL COUNT of COUNT

I have some data I am querying. The table is composed of two columns - a unique ID, and a value. I would like to count the number of times each unique value appears (which can easily be done with a COUNT and GROUP BY), but I then want to be able to count that. So, I would like to see how many items appear twice, three times, etc.
So for the following data (ID, val)...
1, 2
2, 2
3, 1
4, 2
5, 1
6, 7
7, 1
The intermediate step would be (val, count)...
1, 3
2, 3
7, 1
And I would like to have (count_from_above, new_count)...
3, 2 -- since three appears twice in the previous table
1, 1 -- since one appears once in the previous table
Is there any query which can do that? If it helps, I'm working with Postgres. Thanks!
Try something like this:
select
times,
count(1)
from ( select
id,
count(distinct value) as times
from table
group by id ) a
group by times