Displaying and Ordering based off the same column - sql

At the moment, I have my data broken down into month intervals. This is how I want it to be displayed, but I'm trying to only display ordered_by for those that have 300 or more total in the LoadCount for the entire table. So basically, I want to throw out any of the ordered_by that dont have at least 300
SELECT YEAR(stop.actual_arrival) AS Year, MONTH(stop.actual_arrival) AS Month, COUNT(stop.id) AS DeliveryCount, orders.ordered_by, COUNT(DISTINCT orders.id)
AS LoadCount
FROM stop INNER JOIN
(SELECT company_id, order_id, tractor_id
FROM billing_history
GROUP BY order_id, tractor_id, company_id) AS derivedtbl_1 ON stop.company_id = derivedtbl_1.company_id AND stop.order_id = derivedtbl_1.order_id INNER JOIN
tractor ON derivedtbl_1.company_id = tractor.company_id AND derivedtbl_1.tractor_id = tractor.id INNER JOIN
orders ON derivedtbl_1.company_id = orders.company_id AND derivedtbl_1.order_id = orders.id
WHERE (orders.order_type_id IN ('12', '13')) AND (stop.stop_type = 'SO') AND (stop.actual_arrival >= DATEADD(month, - 18, GETDATE())) AND (orders.customer_id = 945000) AND
(orders.ordered_by IS NOT NULL)
GROUP BY YEAR(stop.actual_arrival), MONTH(stop.actual_arrival), orders.ordered_by
ORDER BY Year, Month, orders.ordered_by
I keep going back and forth over whether I need a derived table or what... any help would be really appreciated. Thanks guys.
I'm trying to throw out any of the ordered_by that don't add up to 300 in total. If they add up to 300 across the entire board, I want them to be displayed, even if they're at 5 for that line.

If I understand your question correctly, you could make use of the CASE statement inside your select clause to show NULL or orders.ordered_by depending on the value of LoadCount. Here's a page with some examples: http://msdn.microsoft.com/en-us/library/ms181765(v=sql.110).aspx

If all you are looking to do is throw out any results that don't have ordered_by less than 300, you can use a HAVING clause with the GROUP BY
<snip...>
GROUP BY YEAR(stop.actual_arrival), MONTH(stop.actual_arrival), orders.ordered_by
HAVING orders.ordered_by >= 300
ORDER BY Year, Month, orders.ordered_by

Related

Using a date field for matching SQL Query

I'm having a bit of an issue wrapping my head around the logic of this changing dimension. I would like to associate these two tables below. I need to match the Cost - Period fact table to the cost dimension based on the Id and the effective date.
As you can see - if the month and year field is greater than the effective date of its associated Cost dimension, it should adopt that value. Once a new Effective Date is entered into the dimension, it should use that value for any period greater than said date going forward.
EDIT: I apologize for the lack of detail but the Cost Dimension will actually have a unique Index value and the changing fields to reference for the matching would be Resource, Project, Cost. I tried to match the query you provided with my fields, but I'm getting the incorrect output.
FYI: Naming convention change: EngagementId is Id, Resource is ConsultantId, and Project is ProjectId
I've changed the images below and here is my query
,_cte(HoursWorked, HoursBilled, Month, Year, EngagementId, ConsultantId, ConsultantName, ProjectId, ProjectName, ProjectRetainer, RoleId, Role, Rate, ConsultantRetainer, Salary, amount, EffectiveDate)
as
(
select sum(t.Duration), 0, Month(t.StartDate), Year(t.StartDate), t.EngagementId, c.ConsultantId, c.ConsultantName, c.ProjectId, c.ProjectName, c.ProjectRetainer, c.RoleId, c.Role, c.Rate, c.ConsultantRetainer,
c.Salary, 0, c.EffectiveDate
from timesheet t
left join Engagement c on t.EngagementId = c.EngagementId and Month(c.EffectiveDate) = Month(t.EndDate) and Year(c.EffectiveDate) = Year(t.EndDate)
group by Month(t.StartDate), Year(t.StartDate), t.EngagementId, c.ConsultantName, c.ConsultantId, c.ProjectId, c.ProjectName, c.ProjectRetainer, c.RoleId, c.Role, c.Rate, c.ConsultantRetainer,
c.Salary, c.EffectiveDate
)
select * from _cte where EffectiveDate is not null
union
select _cte.HoursWorked, _cte.HoursBilled, _cte.Month, _cte.Year, _cte.EngagementId, _cte.ConsultantId, _cte.ConsultantName, _cte.ProjectId, _Cte.ProjectName, _cte.ProjectRetainer, _cte.RoleId, _cte.Role, sub.Rate, _cte.ConsultantRetainer,_cte.Salary, _cte.amount, sub.EffectiveDate
from _cte
outer apply (
select top 1 EffectiveDate, Rate
from Engagement e
where e.ConsultantId = _cte.ConsultantId and e.ProjectId = _cte.ProjectId and e.RoleId = _cte.RoleId
and Month(e.EffectiveDate) < _cte.Month and Year(e.EffectiveDate) < _cte.Year
order by EffectiveDate desc
) sub
where _cte.EffectiveDate is null
Example:
I'm struggling with writing the query that goes along with this. At first I attempted to partition by greatest date. However, when I executed the join I got the highest effective date for every single period (even those prior to the effective date).
Is this something that can be accomplished in a query or should I be focusing on incremental updates of the destination table so that any effective date / time period in the past is left alone?
Any tips would be great!
Thanks,
Channing
Try this one:
; with _CTE as(
select p.* , c.EffectiveDate, c.Cost
from period p
left join CostDimension c on p.id = c.id and p.Month = DATEPART(month, c.EffectiveDate) and p.year = DATEPART (year, EffectiveDate)
)
select * from _CTE Where EffectiveDate is not null
Union
select _CTE.id, _CTE.Month, _CTE.Year, sub.EffectiveDate, sub.Cost
from _CTE
outer apply (select top 1 EffectiveDate, Cost
from CostDimension as cd
where cd.Id = _CTE.id and cd.EffectiveDate < DATETIMEFROMPARTS(_CTE.Year, _CTE.Month, 1, 0, 0, 0, 0)
order by EffectiveDate desc
) sub
where _Cte.EffectiveDate is null

Trying to create a SQL query

I am trying to create a query that retrieves only the ten companies with the highest number of pickups over the six-month period, this means pickup occasions, and not the number of items picked up.
I have done this
SELECT *
FROM customer
JOIN (SELECT manifest.pickup_customer_ref reference,
DENSE_RANK() OVER (PARTITION BY manifest.pickup_customer_ref ORDER BY COUNT(manifest.trip_id) DESC) rnk
FROM manifest
INNER JOIN trip ON manifest.trip_id = trip.trip_id
WHERE trip.departure_date > TRUNC(SYSDATE) - interval '6' month
GROUP BY manifest.pickup_customer_ref) cm ON customer.reference = cm.reference
WHERE cm.rnk < 11;
this uses dense_rank to determine the order or customers with the highest number of trips first
Hmm well i don't have Oracle so I can't test it 100%, but I believe your looking for something like the following:
Keep in mind that when you use group by, you have to narrow down to the same fields you group by in the select. Hope this helps at least give you an idea of what to look at.
select TOP 10
c.company_name,
m.pickup_customer_ref,
count(*) as 'count'
from customer c
inner join mainfest m on m.pickup_customer_ref = c.reference
inner join trip t on t.trip_id = m.trip_id
where t.departure_date < DATEADD(month, -6, GETDATE())
group by c.company_name, m.pickup_customer_ref
order by 'count', c.company_name, m.pickup_customer_ref desc

Grouping matching names with totals Firebird 2.5

I did a basic Firebird Report to call on all debtors and transactions
The report looks as follows
SELECT
POSPAY.TXNO,
DEBTORS.COMPANY,
POSPAY.AMOUNT,
POSINVTRANS.TXDATE
FROM
POSPAY
INNER JOIN DEBTORS ON (POSPAY.ACCTNUMBER = DEBTORS.ACCOUNT)
INNER JOIN POSINVTRANS ON (POSPAY.TXNO = POSINVTRANS.TXNO)
WHERE
PAYMNTTYPID = '7'
and
weekly = :weekly and
txdate >= :fromdate and
txdate <= :todate
This works correctly and gives me output on Debtor Name, TXNO, TXDATE, AMOUNT
I now want to write a similar report but need to group the debtors and give totals on the transactions ie I need the output Debtor name (If JOHN is twice, need to list once), Total ammount (Sum of John's transactions)
I still need innerjoin on debtors but no longer on posinvtrans, I was thinking it should look something like
SELECT
POSPAY.TXNO,
DEBTORS.COMPANY,
POSPAY.AMOUNT
FROM
POSPAY
INNER JOIN DEBTORS ON (POSPAY.ACCTNUMBER = DEBTORS.ACCOUNT)
WHERE
PAYMNTTYPID = '7'
and
weekly = :weekly and
txdate >= :fromdate and
txdate <= :todate
Group by DEBTORS.COMPANY
but no luck, get errors on Group by
'invalid expression in the select list (not containing in either an aggregate function or the GROUP BY CLAUSE)'
any suggestions?
The list of fields in the select list have to be either also listed in the group by list or be aggregate functions like count(*), max(amount), etc.
The problem is that you have not told Firebird what to do with POSPAY.TXNO and POSPAY.AMOUNT and it is not sufficient to tell what you do want to happen to them.
I suggest you remove those 2 fields from the query and have a select list of DEBTORS.COMPANY, sum(POSPAY.AMOUNT) as a starting point.
If you use GROUP BY you either need to include a column in the GROUP BY, or apply an aggregate function on the column. In your example you need to leave out POSPAY.TXNO, as that is transaction specific (or you could use the aggregate function LIST), and you need to apply the aggregate function SUM to AMOUNT to get the total:
SELECT
DEBTORS.COMPANY,
SUM(POSPAY.AMOUNT)
FROM
POSPAY
INNER JOIN DEBTORS ON (POSPAY.ACCTNUMBER = DEBTORS.ACCOUNT)
WHERE
PAYMNTTYPID = '7'
and
weekly = :weekly and
txdate >= :fromdate and
txdate <= :todate
Group by DEBTORS.COMPANY

Selecting only if at least one row matches condition

I have a select statement and want to return all values only if at least one of them has a date with 60 days of difference from today.
The problem is that i have an outer apply which returns the column i want to compare to, and they come from different tables (one belongs to cash items, and the other to card items).
Considering I have the following:
OUTER APPLY (
SELECT COUNT(*) AS quantity, MIN(date) AS item_date
FROM dbo.get_cash_items(loans.id_cash) AS cash_item
HAVING loans.id_product_type = 1 --Cash
UNION
SELECT COUNT(*) AS quantity, MIN(date) AS item_date
FROM dbo.get_card_items(loans.id_card) AS card_item
HAVING loans.id_product_type = 2 --Card
) AS items
I want to return all the rows only when DATEDIFF(DAY, MIN(items.item_date), GETDATE()) >= 60, but I want them all even if only one matches this condition.
What would be the best approach to do this?
EDIT
To make it clearer, I'll explain the use case:
I need to show the items of every loan, only if the client is late for more than 60 days of the due date on any of it
I am also not sure, what do you expect, but how about that:
WITH items
AS (SELECT Count(*) AS quantity,
Min(date) AS item_date
FROM dbo.Get_cash_items(loans.id_cash) AS cash_item
HAVING loans.id_product_type = 1
UNION
SELECT Count(*) AS quantity,
Min(date) AS item_date
FROM dbo.Get_card_items(loans.id_card) AS card_item
HAVING loans.id_product_type = 2)
SELECT a.*
FROM items AS a,
(SELECT TOP 1 *
FROM items AS b
WHERE Datediff(day, b.item_date, Getdate()) >= 60) AS c
It's a sort of CROSS JOIN, where table C will have one or zero rows depending on that if the condition is met - it will than join to every row in other table.
Have you tried something like this?
SELECT a.quantity, a.item_date
FROM
(SELECT COUNT(*) AS quantity, MIN(date) AS item_date
FROM dbo.get_cash_items(loans.id_cash) AS cash_item
HAVING loans.id_product_type = 1
UNION
SELECT COUNT(*) AS quantity, MIN(date) AS item_date
FROM dbo.get_card_items(loans.id_card) AS card_item
HAVING loans.id_product_type = 2) a
WHERE DATEDIFF(day, a.item_date, GETDATE()) >= 60
Typically I do this using a CTE to select the key for the records I want to select and then join on that. Below is an attempt at an example:
with LateClients as
(
SELECT LoadId FROM Payment Where /*payment date later than 60 days*/
)
SELECT p.LoanId,
p.UserId
FROM Payment as p
INNER JOIN LateClients as LC
ON p.LoanId = lc.LoanId
OrderBy p.LoanId, p.UserId
I know it's a bit different from the code you posted, but this is a simplified example that should explain the concept. Good luck!

Count records with a criteria like "within days"

I have a table as below on sql.
OrderID Account OrderMethod OrderDate DispatchDate DispatchMethod
2145 qaz 14 20/3/2011 23/3/2011 2
4156 aby 12 15/6/2011 25/6/2011 1
I want to count all records that have reordered 'within 30 days' of dispatch date where Dispatch Method is '2' and OrderMethod is '12' and it has come from the same Account.
I want to ask if this all can be achieved with one query or do I need to create different tables and do it in stages as I think I wll have to do now? Please can someone help with a code/query?
Many thanks
T
Try the following, replacing [tablename] with the name of your table.
SELECT Count(OriginalOrders.OrderID) AS [Total_Orders]
FROM [tablename] AS OriginalOrders
INNER JOIN [tablename] AS Reorders
ON OriginalOrders.Account = Reorders.Account
AND OriginalOrders.OrderDate < Reorders.OrderDate
AND DATEDIFF(day, OriginalOrders.DispatchDate, Reorders.OrderDate) <= 30
AND Reorders.DispatchMethod = '2'
AND Reorders.OrderMethod = '12';
By using an inner join you'll be sure to only grab orders that meet all the criteria.
By linking the two tables (which are essentially the same table with itself using aliases) you make sure only orders under the same account are counted.
The results from the join are further filtered based on the criteria you mentioned requiring only orders that have been placed within 30 days of the dispatch date of a previous order.
Totally possible with one query, though my SQL is a little stale..
select count(*) from table
where DispatchMethod = 2
AND OrderMethod = 12
AND DATEDIFF(day, OrderDate, DispatchDate) <= 30;
(Untested, but it's something similar)
One query can do it.
SELECT COUNT(*)FROM myTable reOrder
INNER JOIN myTable originalOrder
ON reOrder.Account = originalOrder.Account
AND reOrder.OrderID <> originalOrder.OrderID
-- all re-orders that are within 30 days or the
-- original orders dispatch date
AND DATEDIFF(d, originalOrder.DispatchDate, reOrder.OrderDate) <= 30
WHERE reOrder.DispatchMethod = 2
AND reOrder.OrderMethod = 12
You need a self-join.
The query below assumes that a given account will have either 1 or 2 records in the table - 2 if they've reordered, else 1.
If 3 records exist for a given account, 2 orders + 1 reorder then this won't work - but we'd then need more information on how to distinguish between an order and a reorder.
SELECT COUNT(*) FROM myTable new, myTable prev
WHERE new.DispatchMethod = 2
AND new.OrderMethod = 12
AND DATEDIFF(day, prev.DispatchDate, new.OrderDate) <=30
AND prev.Account == new.Account
AND prev.OrderDate < new.OrderDate
Can we use GROUP BY in this case, such as the following?
SELECT COUNT(Account)
FROM myTable
WHERE DispatchMethod = 2 AND OrderMethod = 12
AND DATEDIFF(d, DispatchDate, OrderDate) <=30
GROUP BY Account
Will the above work or am I missing something here?