I'm using Reporting Service in a ASP.NET MVC4 project (using Visual Studio 2010) to generate reports and I'm working with stored procedures. I have a table Product which can belong to persons. That's the concept of allocation. A product can be allocated to different persons (many-to-many relation) and I have an intermediate table between those two :
The thing I want tot do in my report is to display the information related to each existing product including the owner. In this situation, I can have 2 types of owners : the company (so the product has never been allocated or the last allocation has an EndDate) or a person (so the last allocation doesn't have an EndDate or has a "future" EndDate).
I know that all these verifications should be made in my report template but for the SQL stored procedure, I have no idea about how to do to get the last allocation for each product (if there is any).
Any idea which could help is welcomed.
EDIT : Updated query
SELECT pr.SerialNumber, coalesce (p.LastName, 'US') as Owner
FROM bm_Products pr
LEFT OUTER JOIN
(Select Id_Person, Id_Product, max(ISNULL(EndDate,getdate()+1)) as MaxAllocDate
FROM bm_ProductAllocations a
WHERE EndDate > getdate()
group by Id_Person, Id_Product
having max(ISNULL(EndDate, getdate()+1)) =
(select max(isnull(EndDate, getdate()+1))
from bm_ProductAllocations where Id_Product = a.Id_Product)
) pa on pr.Id_Product = pa.Id_Product
LEFT OUTER JOIN bm_Persons p on pa.Id_Person = p.Id_Person
The result :
SerialNumber Owner
-------------------------------------------------- --------------------------------------------------
78745148154815204 US
84512048150410522 US
84512841520415205 US
87451284512485120 US
56123051215215215 US
48512485487487856 US
CNU1510ZL0 US
8456656551521 US
4154854854151 US
4851205230047 US
4511120521050 US
84515151320541201 US
74161230326524165 US
I don't see "company" for owner on the table shown, so i'll assume for now that it is in the products table, a hardcoded 'US' because you own it, or you can provide an update.
SELECT pr.Product, coalesce (p.person, 'US') as Owner
FROM Product pr
LEFT OUTER JOIN
(Select id_person, id_product, max(ISNULL(EndDate,getdate()+1)) as MaxAllocDate
FROM Product_Allocation a
WHERE Enddate > getdate()
group by id_person, id_product
having max(ISNULL(EndDate, getdate()+1)) =
(select max(isnull(Enddate, getdate()+1))
from product_allocation where id_product = a.id_Product)
) pa on pr.id_product = pa.id_product
LEFT OUTER JOIN person p on pa.id_person = p.id_person
The subquery will give you the max end date by product and person. since we need the subquery to return the person, you would possibly get multiple id_person records for each product if you have multiple allocations that haven't expired. The "having" clause will use the already aggregated end date to match to the max end date by product without the person, so you will get only the "most future" allocation end date. Since the product allocation is joined to the person, and they are left joined to the product table, if there are no unexpired or future allocations, or there are no allocations at all, then you will get 'US' as your owner. you can always add in another table join directly from product if your "company" is somewhere else, and substitute that for 'US'.
I don't have a data set to work off of that exactly duplicates this, so I can't test it, but I did test the having process and it functioned correctly.
if there are other assumption like, only one allocation at a time, etc, this could be simplified. Also you could work this through a memory table with only current and future allocations by product, but I believe this should work. You may need to adjust the 'getdate()' if you are working off date only not date time. If that's the case then use cast(convert(varchar(10), getdate(), 101) as datetime) and return only the date portion of the getdate function.
Hope this helps you out
Related
let's say we have a table with invoices and employees ID
and another table for vacation which also contains an employee ID.
In this case only emplyee1 had vacation a these date frames.
Now we have to compare if the invoice date is between or exactly at the date frames of vacation table.
The aim is: The invoice date has to be compared, if an employee is on vacation the calc_flag = 0, if not then always=1. E.g. employee1 was from 2023/01/05 till 2023/01/12 on vacation.
So all his invoices must be calc_flag=0 for this time.
How to create the sql query in mssql for this topic?
Thanks for any advice.
I already tried to check the dateframes but if there are several entries in vacation table, I'm not sure how to handle it.
One way to do this is a LEFT JOIN to check for existance. Perhaps you need to tweak the < or > depening if you would like to include the Begin and or End date.
SELECT i.InvNo,
i.emp_ID,
i.InvDate,
i.calc_flag ,
CASE WHEN v.Emp_ID IS NOT NULL THEN 0 ELSE 1 END AS calc_flage
FROM dbo.invoices AS i
LEFT JOIN dbo.vacation AS v ON v.Emp_ID = i.emp_ID AND v.[Begin] < i.InvDate AND v.[End] > i.InvDate
I am using Sql Server Management studio.
I have the following table.
when i run the following query I get the running total for sales column
select s1.date,
sum(s2.sales)
from sales s1
join sales s2 on s1.date>=s2.date
group by s1.date;
but when i substitute s2.sales with s1.sales in the select
select s1.date,
**sum(s1.sales)**
from sales s1
join sales s2 on s1.date>=s2.date
group by s1.date;
it gives me a different answer can someone help me understand why i am facing this? since the sales column value should be the same.
The first version of your running total query is summing sales, for each dates, over dates which are strictly less than or equal to the date in each record. When you change s2.sales to s1.sales, you are then summing the current record's sales N number of times, where N is the number of records having an earlier date. This clearly is not the logic you want, so stick with the first version.
By the way, if you're using MySQL 8+, then analytic functions simplify things even further:
SELECT Date, Sales, SUM(Sales) OVER (ORDER BY Date) RunningSales
FROM sales
ORDER BY Date;
This is the question using AdvetureWorks2012.
Create a VIEW dbo.vw_Commissions to display the commissions earned last
year by all sales employees. Round the result set to two decimal places and do not include any salesperson who did not earn a commission. Include the
salesperson name, the commission earned, and the job title. Concatenate the
salesperson first and last names.
This code is not working for me. What am I screwing up?
USE AdventureWorks2012
GO
CREATE VIEW dbo.vw_Commissions
AS
SELECT
Sales.SalesPerson.SalesLastYear,
Person.Person.LastName,
Person.Person.FirstName,
HumanResources.Employee.JobTitle
FROM
Sales.SalesPerson
LEFT OUTER JOIN
Sales.SalesPerson ON Sales.SalesPerson.BusinessEntityID = Person.Person.BusinessEntityID
LEFT OUTER JOIN
Person.Person ON Person.Person.BusinessEntityID = HumanResources.Employee.BusinessEntityID
There are multiple problems with your query.
The biggest problem is it is not answering the questions asked. You are selecting SalesLastYear whereas the question asks to calculate the Commissions.
You are not filtering SalesPersons which have not earned any commission.
You need this to run only for last year.
Concatenate the FirstName and LastName.
The error you are getting is because you are using Sales.SalesPerson twice in your query. You need to give them alias names. However, I don't think you need two instances of the same table in this query. Also to use HumanResources.Employee.JobTitle column in the select list, you need to include table HumanResources.Employee in the from list.
I'm sorry, I'm newbie using T-SQL AND I would like to know how can i get value that doesn't occurs more than once. I already tried this but it didn't work.
SELECT DISTINCT *
FROM Orders
WHERE PmtType NOT IN ('VISA','DISC','FUNDING','DEALER CHECK','MC'
,'AMEX','BONUS POOL','DLR CK - NET30'
,'WIRE','MO','EXCHANGE','ONLINE','NULL')
AND OrderDate BETWEEN '2014-03-01 00:00:00'
and '2015-03-01 00:00:00'
AND CompanyId IN ('1311','8390','8394','8396','8397','8399','3966',
'8407','8408','8315','8411','8413','8414','8416'
,'8419','4850','8426','8428','8429','8430')
What I'm trying to get is this. Companies that receive Free Demos. Which the PmtType would be free, but never purchased a product.
If the customer never purchase a product the customer Id shouldn't appear in the
PmtType IN ('VISA','DISC','FUNDING','DEALER CHECK'
,'MC' ,'AMEX','BONUS POOL','DLR CK - NET30'
,'WIRE','MO','EXCHANGE','ONLINE','NULL')
if i read the question correctly you want to know which IDs have only 1 order, this will do it. I used generic field name as you didnt specify what "value" you want to find...
EDIT: added the NOT EXISTS clause after OP Comment, you may no longer need the group by, that is up to you...
SELECT CompanyId --add fields here as needed.
,Count(*) [Occurences]
FROM Orders o
WHERE PmtType = 'FREE'
AND NOT EXISTS (SELECT CompanyId
FROM Orders io
WHERE o.CompanyId = io.CompanyId
AND PmtType <> 'FREE' )
GROUP BY CompanyId --add fields here as needed.
HAVING Count(*) = 1 --leave this out to see how many free demos each company got.
Unit - hmy, scode, hProperty
InsurancePolicy - hmy, hUnit, dtEffective, sStatus
Select MAX(i2.dtEffective) as maxdate, u.hMy, MAX(i2.hmy) as InsuranceId,
i2.sStatus
from unit u
left join InsurancePolicy i2 on i2.hUnit = u.hMy
and i2.sStatus in ('Active', 'Cancelled', 'Expired')
where u.hProperty = 2
Group By u.hmy, i2.sStatus
order by u.hmy
This query will return values for the Insurance Policy with the latest Effective Date (Max(dtEffective)). I added Max(i2.hmy) so if there was more than one Insurance Policy for the latest Effective Date, it will return the one with the highest ID (i2.hmy) in the database.
Suppose there was a Unit that had 3 Insurance Policies attached with the same latest effective date and all have different sStatus'.
The result would look like this:
maxdate UnitID InsuranceID sStatus
1/23/12 2949 1938 'Active'
1/23/12 2949 2343 'Cancelled'
1/23/12 2949 4323 'Expired'
How do I filter the results so that if there are multiple Insurance Policies with different Status' for the same unit and same date, then we choose the Insurance Policy with the 'Active' Status first, if one doesn't exist, choose 'Cancelled', and if that doesn't exist, choose 'Expired'.
This seems to be a matter of proper ranking of InsurancePolicy's rows and then joining Unit to the set of the former's top-ranked rows:
;
WITH ranked AS (
SELECT
*,
rnk = ROW_NUMBER() OVER (
PARTITION BY hUnit
ORDER BY dtEffective DESC, sStatus, hmy DESC
)
FROM InsurancePolicy
)
SELECT
i2.dtEffective AS maxdate,
u.hMy,
i2.hmy AS InsuranceId,
i2.sStatus
FROM Unit u
LEFT JOIN ranked i2 ON i2.hUnit = u.hMy AND i2.rnk = 1
You could make this work with one SQL statement but it will be nearly unreadable to your everyday t-sql developer. I would suggest breaking this query up into a few steps.
First, I would declare a table variable and place all the records that require no manipulation into this table (ie - Units that do not have multiple statuses for the same date = good records).
Then, get a list of your records that need work done on them (multiple statuses on the same date for the same UnitID) and place them in a table variable. I would create a "rank" column within this table variable using a case statement as illustrated here:
Pseudocode: WHEN Active THEN 1 ELSE WHEN Cancelled THEN 2 ELSE WHEN Expired THEN 3 END
Then delete records where 2 and 3 exist with a 1
Then delete records where 2 exists and 3
Finally, merge this updated table variable with your table variable containing your "good" records.
It is easy to get sucked into trying to do too much within one SQL statement. Break up the tasks to make it easier for you to develop and more manageable in the future. If you have to edit this SQL in a few years time you will be thanking yourself, not to mention any other developers that may have to take over your code.