Need a little help, SQL Server 2000, having and max(date) - sql

I need a little help getting this script doing what I want it to.
SELECT KM.initials,
TPL.debprice,
TMP.pricegroup,
TMP.date
FROM klientmedarbejder KM
INNER JOIN timeprismedarbejderprisgruppe TMP
ON KM.employeeno = TMP.employeeno
INNER JOIN timeprisprisgruppelinie TPL
ON TMP.pricegroup = TPL.pricegroup
GROUP BY KM.initials,
TMP.date,
TPL.debprice,
TMP.pricegroup,
TPL.date
HAVING ( Max(TMP.dato) = TPL.date )
What I need is the debPrice with the latest (max) date. An employee goes through his career, once in a while his price gets upgraded. Price gets selected from a set list of prices.
KlientMedarbejder is the employee table.
TimeprisMedarbejderPrisgruppe is the list of date his record get upgrade to the new price.
TimeprisPrisgruppeLinie is the list of prices you can have.
I have the solution down to 2 options per employee. Hence:
emp A - 300 - 9 - 1900-01-01
emp A - 500 - 4 - 2012-01-01
emp B - 400 - 6 - 1900-01-01
emp B - 800 - 8 - 2012-04-01
Hence, first record is the default from whenever he joined the company. In 2012 he was finally good enough for a better price tag. Now I need the answer with the latest date on the upgrade list for each employee. Hence emp A should give me 500 and emp B should give me 800. Now not all employees may have had the 01-01 or 04-01 price update. Some employees have had up to 8 upgrades over time, I only care for the latest.
Client I'm pulling this data from is running an SQL Server 2000.

The typical solution for SQL Server 2000 is to isolate the employeeno and their max date (this is much easier in 2005+). Pseudo-code:
SELECT d.columns, etc. FROM
(
SELECT employeeno, [date] = MAX([date])
FROM dbo.tablewithdates
GROUP BY employeeno
) AS grouped_emp
INNER JOIN dbo.tablewithdates AS d
ON d.employeeno = grouped_emp.employeeno
AND d.[date] = grouped_emp.[date]
INNER JOIN ...
The danger is if employeeno + date is not unique, you could get multiple rows, so you need to determine how to deal with ties.
I would write it for your schema but I can't reverse engineer your query to figure out which table is which. Start simple and work up.

The HAVING clause takes place after the group by, so it can only use aggregated columns.
You need to calcualte the max date before the group by. This requires adding in a subquery for the calculation and then a WHERE clause to do the right filtering:
SELECT KM.initials, TPL.debprice, TMP.pricegroup, TMP.date
FROM klientmedarbejder KM INNER JOIN
timeprismedarbejderprisgruppe TMP
ON KM.employeeno = TMP.employeeno INNER JOIN
timeprisprisgruppelinie TPL
ON TMP.pricegroup = TPL.pricegroup join
(select employeeno, MAX(dato) as maxdato
from timeprismedarbejderprisgruppe t
group by employeeno
) tmax
on tmax.employeeno = tmp.employeeno
where tmp.dato = tmax.maxdato
GROUP BY KM.initials, TMP.date, TPL.debprice, TMP.pricegroup, TPL.date

Related

SQL SELECT filtering out combinations where another column contains empty cells, then returning records based on max date

I have run into an issue I don't know how to solve. I'm working with a MS Access DB.
I have this data:
I want to write a SELECT statement, that gives the following result:
For each combination of Project and Invoice, I want to return the record containing the maximum date, conditional on all records for that combination of Project and Invoice being Signed (i.e. Signed or Date column not empty).
In my head, first I would sort the irrelevant records out, and then return the max date for the remaining records. I'm stuck on the first part.
Could anyone point me in the right direction?
Thanks,
Hulu
Start with an initial query which fetches the combinations of Project, Invoice, Date from the rows you want returned by your final query.
SELECT
y0.Project,
y0.Invoice,
Max(y0.Date) AS MaxOfDate
FROM YourTable AS y0
GROUP BY y0.Project, y0.Invoice
HAVING Sum(IIf(y0.Signed Is Null,1,0))=0;
The HAVING clause discards any Project/Invoice groups which include a row with a Null in the Signed column.
If you save that query as qryTargetRows, you can then join it back to your original table to select the matching rows.
SELECT
y1.Project,
y1.Invoice,
y1.Desc,
y1.Value,
y1.Signed,
y1.Date
FROM
YourTable AS y1
INNER JOIN qryTargetRows AS sub
ON (y1.Project = sub.Project)
AND (y1.Invoice = sub.Invoice)
AND (y1.Date = sub.MaxOfDate);
Or you can do it without the saved query by directly including its SQL as a subquery.
SELECT
y1.Project,
y1.Invoice,
y1.Desc,
y1.Value,
y1.Signed,
y1.Date
FROM
YourTable AS y1
INNER JOIN
(
SELECT y0.Project, y0.Invoice, Max(y0.Date) AS MaxOfDate
FROM YourTable AS y0
GROUP BY y0.Project, y0.Invoice
HAVING Sum(IIf(y0.Signed Is Null,1,0))=0
) AS sub
ON (y1.Project = sub.Project)
AND (y1.Invoice = sub.Invoice)
AND (y1.Date = sub.MaxOfDate);
Write A SQL query, which should be possible in MS-Access too, like this:
SELECT
Project,
Invoice,
MIN([Desc]) Descriptions,
SUM(Value) Value,
MIN(Signed) Signed,
MAX([Date]) "Date"
FROM data
WHERE Signed<>'' AND [Date]<>''
GROUP BY
Project,
Invoice
output:
Project
Invoice
Descriptions
Value
Signed
Date
A
1
Ball
100
J.D.
2022-09-20
B
1
Sofa
300
J.D.
2022-09-22
B
2
Desk
100
J.D.
2022-09-23
Note: for invoice 1 on project A, you will see a value of 300, which is the total for that invoice (when grouping on Project='A' and Invoice=1).
Maybe I should have used DCONCAT (see: Concatenation in between records in Access Query ) for the Description, to include 'TV' in it. But I am unable to test that so I am only referring to this answer.
Try joining a second query:
Select *
From YourTable As T
Inner Join
(Select Project, Invoice, Max([Date]) As MaxDate
From YourTable
Group By Project, Invoice) As S
On T.Project = S.Project And T.Invoice = S.Invoice And T.Date = S.MaxDate

Use query result

I´m having issues with the following query. I have two tables; Table Orderheader and table Bought. The first query I execute gives me, for example, two dates. Based on these two dates, I need to find Production data AND, based on the production data, I need to find the Bought data, and combine those data together. Lets say I do the following:
Select Lotdate From Orderheader where orhsysid = 1
This results in two rows: '2019-02-05' and '2019-02-04'. Now I need to do two things: I need two run two queries using this result set. The first one is easy; use the dates returned and get a sum of column A like this:
Select date, SUM(Amount) from Orderheader where date = Sales.date() [use the two dates here]
The second one is slighty more complicated, I need to find the last day where something has been bought based on the two dates. Production is everyday so Productiondate=Sales.date()-1. But Bought is derived from Productionday and is not everyday so for every Productionday it needs to find the last Boughtday. So I can't say where date = Orderheader.date. I need to do something like:
Select date, SUM(Amount)
FROM Bought
WHERE date = (
SELECT top 1 date
FROM Bought
WHERE date < Orderheader.date)
But twice, for both the dates I got.
This needs to result in 1 table giving me:
Bought.date, Bought.SUM(AMOUNT), Orderheader.date, Orderheader.SUM(AMOUNT)
All based on the, possible multiple, Lotdate(s) I got from the first query from Sales table.
I've been struggling with this for a moment now, using joins and nested queries but I can't seem to figure it out!
Example sample:
SELECT CONVERT(date,ORF.orfDate) as Productiedatum, SUM(orlQuantityRegistered) as 'Aantal'
FROM OrderHeader ORH
LEFT JOIN OrderFrame ORF ON ORH.orhFrameSysID = ORF.orfSysID
LEFT JOIN OrderLine ORL ON ORL.orhSysID = ORH.orhSysID
LEFT JOIN Item ON Item.itmSysID = ORL.orlitmSysID
where CONVERT(date,ORF.orfDate) IN
(
SELECT DISTINCT(CONVERT(date, Lot.lotproductiondate)) as Productiedatum
FROM OrderHeader ORH
LEFT JOIN Registration reg ON reg.regorhSysID = ORH.orhSysID
LEFT JOIN StockRegistration stcreg ON stcreg.stcregRegistrationSysID = reg.regSysID
LEFT JOIN Lot ON Lot.lotSysID = stcregSrclotSysID
WHERE ORH.orhSysID = 514955
AND regRevokeRegSysID IS NULL
AND stcregSrcitmSysID = 5103
)
AND ORL.orlitmSysID = 5103
AND orldirSysID = 2
AND NOT orlQuantityRegistered IS NULL
GROUP BY Orf.orfDate
Sample output:
Productiedatum Aantal
2019-02-05 20
2019-02-06 20
Here I used a nested subquery to get the results from 'Production' (orderheader) because I just can use date = date. I'm struggling with the Sales part where I need to find the last date(s) and use those dates in the Sales table to get the sum of that date.
Expected output:
Productiedatum Aantal Boughtdate Aantal
2019-02-04 20 2019-02-01 55
2019-02-05 20 2019-02-04 60
Try this.
IF OBJECT_ID('tempdb..#Production') IS NOT NULL DROP TABLE #Production
IF OBJECT_ID('tempdb..#Bought') IS NOT NULL DROP TABLE #Bought
CREATE table #Production(R_NO int,ProductionDate datetime,ProductionAmount float)
CREATE table #Bought(R_NO int,Boughtdate datetime,Boughtamount float)
insert into #Production(ProductionDate,ProductionAmount,R_NO)
select p.date ProductionDate,sum(Amount) ProductionAmount,row_number()over (order by p.date) R_NO
from Production P
join Sales s on p.date=S.date-1
where orhsysid=1
group by p.date
declare #loop int,#ProdDate datetime
select #loop =max(R_NO) from #Production
while (1<=#loop)
begin
select #ProdDate=ProductionDate from #Production where r_no=#loop
insert into #Bought(Boughtdate,Boughtamount,R_NO)
select Date,Sum(Amount),#loop R_NO from Bought where date=(
select max(date) from bought B
where B.Date<#ProdDate)
group by Date
set #loop=#loop-1
end
select ProductionDate,ProductionAmount,Boughtdate,Boughtamount from #Bought B
join #Production p on B.R_NO=P.R_NO

SQL aggregate functions and sorting

I am still new to SQL and getting my head around the whole sub-query aggregation to display some results and was looking for some advice:
The tables might look something like:
Customer: (custID, name, address)
Account: (accountID, reward_balance)
Shop: (shopID, name, address)
Relational tables:
Holds (custID*, accountID*)
With (accountID*, shopID*)
How can I find the store that has the least reward_balance?
(The customer info is not required at this point)
I tried:
SELECT accountID AS ACCOUNT_ID, shopID AS SHOP_ID, MIN(reward_balance) AS LOWEST_BALANCE
FROM Account, Shop, With
WHERE With.accountID = Account.accountID
AND With.shopID=Shop.shopID
GROUP BY
Account.accountID,
Shop.shopID
ORDER BY MIN(reward_balance);
This works in a way that is not intended:
ACCOUNT_ID | SHOP_ID | LOWEST_BALANCE
1 | 1 | 10
2 | 2 | 40
3 | 3 | 100
4 | 4 | 1000
5 | 4 | 5000
As you can see Shop_ID 4 actually has a balance of 6000 (1000+5000) as there are two customers registered with it. I think I need to SUM the lowest balance of the shops based on their balance and display it from low-high.
I have been trying to aggregate the data prior to display but this is where I come unstuck:
SELECT shopID AS SHOP_ID, MIN(reward_balance) AS LOWEST_BALANCE
FROM (SELECT accountID, shopID, SUM(reward_balance)
FROM Account, Shop, With
WHERE
With.accountID = Account.accountID
AND With.shopID=Shop.shopID
GROUP BY
Account.accountID,
Shop.shopID;
When I run something like this statement I get an invalid identifier error.
Error at Command Line : 1 Column : 24
Error report -
SQL Error: ORA-00904: "REWARD_BALANCE": invalid identifier
00904. 00000 - "%s: invalid identifier"
So I figured I might have my joining condition incorrect and the aggregate sorting incorrect, and would really appreciate any general advice.
Thanks for the lengthy read!
Approach this problem one step at time.
We're going to assume (and we should probably check this) that by least reward_balance, that refers to the total of all reward_balance associated with a shop. And we're not just looking for the shop that has the lowest individual reward balance.
First, get all of the individual "reward_balance" for each shop. Looks like the query would need to involve three tables...
SELECT s.shop_id
, a.reward_balance
FROM `shop` s
LEFT
JOIN `with` w
ON w.shop_id = s.shop_id
LEFT
JOIN `account` a
ON a.account_id = w.account_id
That will get us the detail rows, every shop along with the individual reward_balance amounts associated with the shop, if there are any. (We're using outer joins for this query, because we don't see any guarantee that a shops is going to be related to at least one account. Even if it's true for this use case, that's not always true in the more general case.)
Once we have the individual amounts, the next step is to total them for each shop. We can do that using a GROUP BY clause and a SUM() aggregate.
SELECT s.shop_id
, SUM(a.reward_balance) AS tot_reward_balance
FROM `shop` s
LEFT
JOIN `with` w
ON w.shop_id = s.shop_id
LEFT
JOIN `account` a
ON a.account_id = w.account_id
GROUP BY s.shop_id
At this point, with MySQL we could add an ORDER BY clause to arrange the rows in ascending order of tot_reward_balance, and add a LIMIT 1 clause if we only want to return a single row. We can also handle the case when tot_reward_balance is NULL, assigning a zero in place of the NULL.
SELECT s.shop_id
, IFNULL(SUM(a.reward_balance),0) AS tot_reward_balance
FROM `shop` s
LEFT
JOIN `with` w
ON w.shop_id = s.shop_id
LEFT
JOIN `account` a
ON a.account_id = w.account_id
GROUP BY s.shop_id
ORDER BY tot_reward_amount ASC, s.shop_id ASC
LIMIT 1
If there are two (or more) shops with the same least value of tot_reward_amount, this query returns only one of those shops.
Oracle doesn't have the LIMIT clause like MySQL, but we can get equivalent result using analytic function (which is not available in MySQL). We also replace the MySQL IFNULL() function with the Oracle equivalent NVL() function...
SELECT v.shop_id
, v.tot_reward_balance
, ROW_NUMBER() OVER (ORDER BY v.tot_reward_balance ASC, v.shop_id ASC) AS rn
FROM (
SELECT s.shop_id
, NVL(SUM(a.reward_balance),0) AS tot_reward_balance
FROM shop s
LEFT
JOIN with w
ON w.shop_id = s.shop_id
LEFT
JOIN account a
ON a.account_id = w.account_id
GROUP BY s.shop_id
) v
HAVING rn = 1
Like the MySQL query, this returns at most one row, even when two or more shops have the same "least" total of reward_balance.
If we want to return all of the shops that have the lowest tot_reward_balance, we need to take a slightly different approach.
The best approach to building queries is step wise refinement; in this case, start by getting all of the individual reward_amount for each shop. Next step is to aggregate the individual reward_amount into a total. The next steps is to pickout the row(s) with the lowest total reward_amount.
In SQL Server, You can try using a CTE:
;with cte_minvalue as
(
select rank() over (order by Sum_Balance) as RowRank,
ShopId,
Sum_Balance
from (SELECT Shop.shopID, SUM(reward_balance) AS Sum_Balance
FROM
With
JOIN Shop ON With.ShopId = Shop.ShopId
JOIN Account ON With.AccountId = Account.AccountId
GROUP BY
Shop.shopID)ShopSum
)
select ShopId, Sum_Balance from cte_minvalue where RowRank = 1

SQL Server get customer with 7 consecutive transactions

I am trying to write a query that would get the customers with 7 consecutive transactions given a list of CustomerKeys.
I am currently doing a self join on Customer fact table that has 700 Million records in SQL Server 2008.
This is is what I came up with but its taking a long time to run. I have an clustered index as (CustomerKey, TranDateKey)
SELECT
ct1.CustomerKey,ct1.TranDateKey
FROM
CustomerTransactionFact ct1
INNER JOIN
#CRTCustomerList dl ON ct1.CustomerKey = dl.CustomerKey --temp table with customer list
INNER JOIN
dbo.CustomerTransactionFact ct2 ON ct1.CustomerKey = ct2.CustomerKey -- Same Customer
AND ct2.TranDateKey >= ct1.TranDateKey
AND ct2.TranDateKey <= CONVERT(VARCHAR(8), (dateadd(d, 6, ct1.TranDateTime), 112) -- Consecutive Transactions in the last 7 days
WHERE
ct1.LogID >= 82800000
AND ct2.LogID >= 82800000
AND ct1.TranDateKey between dl.BeginTranDateKey and dl.EndTranDateKey
AND ct2.TranDateKey between dl.BeginTranDateKey and dl.EndTranDateKey
GROUP BY
ct1.CustomerKey,ct1.TranDateKey
HAVING
COUNT(*) = 7
Please help make it more efficient. Is there a better way to write this query in 2008?
You can do this using window functions, which should be much faster. Assuming that TranDateKey is a number and you can subtract a sequential number from it, then the difference constant for consecutive days.
You can put this in a query like this:
SELECT CustomerKey, MIN(TranDateKey), MAX(TranDateKey)
FROM (SELECT ct.CustomerKey, ct.TranDateKey,
(ct.TranDateKey -
DENSE_RANK() OVER (PARTITION BY ct.CustomerKey, ct.TranDateKey)
) as grp
FROM CustomerTransactionFact ct INNER JOIN
#CRTCustomerList dl
ON ct.CustomerKey = dl.CustomerKey
) t
GROUP BY CustomerKey, grp
HAVING COUNT(*) = 7;
If your date key is something else, there is probably a way to modify the query to handle that, but you might have to join to the dimension table.
This would be a perfect task for a COUNT(*) OVER (RANGE ...), but SQL Server 2008 supports only a limited syntax for Windowed Aggregate Functions.
SELECT CustomerKey, MIN(TranDateKey), COUNT(*)
FROM
(
SELECT CustomerKey, TranDateKey,
dateadd(d,-ROW_NUMBER()
OVER (PARTITION BY CustomerKey
ORDER BY TranDateKey),TranDateTime) AS dummyDate
FROM CustomerTransactionFact
) AS dt
GROUP BY CustomerKey, dummyDate
HAVING COUNT(*) >= 7
The dateadd calculates the difference between the current TranDateTime and a Row_Number over all date per customer. The resulting dummyDatehas no actual meaning, but is the same meaningless date for consecutive dates.

How to get records from both tables using ms access query

I have 2 Tables in Ms Access
tbl_Master_Employess
tbl_Emp_Salary
I want to show all the employees in the employee table linked with employee salary table
to link both table the id is coluqEmpID in both table
In the second table, I have a date column. I need a query which should fetch records from both tables using a particular date
I tried the following query:
select coluqEID as EmployeeID , colEName as EmployeeName,"" as Type, "" as Amt
from tbl_Master_Employee
union Select b.coluqEID as EmployeeID, b.colEName as EmployeeName, colType as Type, colAmount as Amt
from tbl_Emp_Salary a, tbl_Master_Employee b
where a.coluqEID = b.coluqEID and a.colDate = #12/09/2013#
However, it shows duplicates.
Query4
EmployeeID EmployeeName Type Amt
1 LAKSHMANAN
1 LAKSHMANAN Advance 100
2 PONRAJ
2 PONRAJ Advance 200
3 VIJAYAN
4 THIRUPATHI
5 VIJAYAKUMAR
6 GOVINDAN
7 TAMILMANI
8 SELVAM
9 ANAMALAI
10 KUMARAN
How would I rewrite my query to avoid duplicates, or what would be a different way to not show duplicates?
The problem with your query is that you are using union when what you want is a join. The union is first going to list all employees with the first part:
select coluqEID as EmployeeID , colEName as EmployeeName,"" as Type, "" as Amt
from tbl_Master_Employee
and then adds to that list all employee records where they have a salary with a certain date.
Select b.coluqEID as EmployeeID, b.colEName as EmployeeName, colType as Type,
colAmount as Amt
from tbl_Emp_Salary a, tbl_Master_Employee b
where a.coluqEID = b.coluqEID and a.colDate = #12/09/2013#
Is your goal to get a list of all employees and only display salary information for those who have a certain date? Some sample data would be useful. Assuming the data here: SQL Fiddle this query should create what you want.
Select a.coluqEID as EmployeeID, colEName as EmployeeName,
b.colType as Type, b.colAmount as Amt
FROM tbl_Master_Employees as a
LEFT JOIN (select coluqEID, colType, colAmount FROM tbl_EMP_Salary
where colDate = '20130912') as b ON a.coluqEID = b.coluqEID;
The first step is to create a select that will get you just the salaries that you want by date. You can then perform a join on this as if you were performing a separate query. You use a LEFT JOIN because you want all of the records from one side, the employees, and only the records that match your criteria from the second side, your salaries.
I believe you will need a join, however as to your question on Unique names.
select **DISTINCT** coluqEID as EmployeeID
Adding the distinct operator would give only uniquely returned results.