SQL query select geography point - sql-server

I'm new in SQL querying and I need to get the last position of some players that are active, meaning they have Play value equal to 1. I have to make a join between the table Events where I have the players activity with columns:
ID: unique row id
Timestamp: time when the player changed his status for active to inactive or active again
PlayerId: id of the player that this event is for
Active: 1-Active, 0-Inactive
with the table Positions where I have all players position at every 2-3 seconds with columns:
PlayerId
Timestamp: time when the position was received
Location: a geography point with the received position
in order to get the current latitude and longitude of the players that are active.
My current query:
select
e.PlayerId, e.Active,
p.Location.Lat, p.Location.Long,
max_date = max(e.Timestamp),
max_date2 = max(p.Timestamp)
from
Events e
inner join
Positions p on e.PlayerId = p.PlayerId
where
e.Active= 1
group by
e.PlayerId, e.Active, p.Location.Lat, p.Location.Long
but instead of returning 2 rows I get much more. I guess it's because of the b.Location.Lat, b.Location.Long fields inside the group by clause because the simple query:
select
e.PlayerId, e.Active,
max_date = max(e.Timestamp),
max_date2 = max (p.Timestamp)
from
Events e
inner join
Positions p on e.PlayerId = p.PlayerId
where
e.Active = 1
group by
e.PlayerId, e.Active
returns the correct 2 rows but I need to also get the Lat-Long columns.
UPDATE
I found an issue inside my query. When I've run it again for different values I've seen that it returns all the players position if they were even only once active and after that they got inactive. But if the last value for Active (according to the maximum timestamp) for one user is 0, then the query should remove the player location from the response.
Is there any way that I can add those columns without getting more rows than needed?

You could wrap your current query in an outer query, then join to your positions table again. Something like this;
SELECT
base.PlayerId
,base.Active
,base.max_date
,base.max_date2
,p.Location.lat
,p.Location.long
FROM
(
SELECT a.PlayerId ,
a.Active,
max_date = max( a.Timestamp ),
max_date2 = max (b.Timestamp)
FROM Events a
INNER JOIN Positions b
ON a.PlayerId =b.PlayerId
WHERE a.Active= 1
GROUP BY a.PlayerId , a.Active
) base
JOIN Positions p
ON base.PlayerId = p.PlayerId
AND base.max_date2 = p.Timestamp
The reason your other query wasn't working is that you're going to have an entry for each lat & long point. Doing this will give you the unique list that you're after, then joins to Positions again to get lat long info.
Edit: As per the comments, if you want to exclude anybody with the latest Active value set to zero then add this to the end of the code;
JOIN
(
SELECT
e.PlayerID
,MAX(e.Timestamp) Timestamp
FROM Events e
GROUP BY e.PlayerID
) latest
ON base.PlayerID = latest.PlayerID
JOIN Events e2
ON latest.PlayerID = e2.PlayerID
AND latest.Timestamp = e2.Timestamp
WHERE e2.Active <> 0

Related

How to display only the MAX results from a query

I am new to writing MS SQL queries and I am trying to display only the record with the highest field named RecordVersion.
Below is the query that works but displays all records:
SELECT
PriceCalendars.PriceProgramID,
PriceCalendars.EffectiveDateTime,
PriceSchedules.Price,
PriceSchedules.PLU,
items.Descr,
PriceSchedules.LastUpdate,
PriceSchedules.LastUpdatedBy,
PriceSchedules.RecordVersion,
PriceSchedules.PriceScheduleUniqueID
FROM
PriceCalendars
INNER JOIN PriceSchedules ON PriceCalendars.PriceProgramID = PriceSchedules.PriceProgramID
INNER JOIN items ON PriceSchedules.PLU = items.PLU
WHERE
(PriceSchedules.PLU = 'SLS10100103')
AND (PriceCalendars.EffectiveDateTime = '2016-03-22')
Here are the query results:
PriceProgramID EffectiveDateTime Price PLU Descr LastUpdate LastUpdatedBy RecordVersion PriceScheduleUniqueID
1 2016-03-22 00:00:00.000 35.00 SLS10100103 Architecture Adult from NP POS 2015-01-22 07:53:15.000 GX70,83 9 569
1 2016-03-22 00:00:00.000 32.00 SLS10100103 Architecture Adult from NP POS 2014-02-25 16:22:46.000 GX70,83 5 86180
The first line of the results has RecordVersion being 9 and the second line results is 5, I only want the higher record displaying, the one that returned RecordVersion = 9.
Every time I try to use the MAX command I get errors or the group by and I have tried every example I could find on the web but nothing seems to work.
Using MS SQL 2012.
Thanks,
Ken
Try the following query which attempts to solve your problem by ordering the returned rows by RecordVersion DESC and then SELECTs just the first row.
SELECT TOP 1
PriceCalendars.PriceProgramID,
PriceCalendars.EffectiveDateTime,
PriceSchedules.Price,
PriceSchedules.PLU,
items.Descr,
PriceSchedules.LastUpdate,
PriceSchedules.LastUpdatedBy,
PriceSchedules.RecordVersion,
PriceSchedules.PriceScheduleUniqueID
FROM
PriceCalendars
INNER JOIN PriceSchedules ON PriceCalendars.PriceProgramID = PriceSchedules.PriceProgramID
INNER JOIN items ON PriceSchedules.PLU = items.PLU
WHERE
(PriceSchedules.PLU = 'SLS10100103')
AND (PriceCalendars.EffectiveDateTime = '2016-03-22')
ORDER BY
RecordVersion DESC
All group by columns should be in select ,that's the rule of group by.How group by works is for every distinct combination of group by columns,arrange remaining columns into groups,so that any aggregation can be applied,in your case I am not sure what group by columns are unique with out test date.here is one version which use row number which gives you the output desired
Remember ,order by last updated date is the one which decides rows order and assign numbers
WITH CTE
AS
(
SELECT PriceCalendars.PriceProgramID,
PriceCalendars.EffectiveDateTime,
PriceSchedules.Price,
PriceSchedules.PLU,
items.Descr,
PriceSchedules.LastUpdate,
PriceSchedules.LastUpdatedBy,
PriceSchedules.RecordVersion,
PriceSchedules.PriceScheduleUniqueID,
ROW_NUMBER() OVER (PARTITION BY PriceSchedules.RecordVersion ORDER BY PriceSchedules.LastUpdatedBy) AS RN
FROM
PriceCalendars
INNER JOIN PriceSchedules ON PriceCalendars.PriceProgramID = PriceSchedules.PriceProgramID
INNER JOIN items ON PriceSchedules.PLU = items.PLU
WHERE
(PriceSchedules.PLU = 'SLS10100103')
AND (PriceCalendars.EffectiveDateTime = '2016-03-22')
)
SELECT * FROM CTE WHERE RN=1

Sql Group by and Ordering

I have three tables. 1)Category table, 2)table having Reports belonging to different categories and 3)an event table which keeps track of reports being accessed. I’m writing a stored procedure to rank and display either top ten or all the reports Ordered by category, reports belonging to which have gotten the maximum number of hits. Trend is calculated by comparing the hits received by report in the current period to the hits received by previous period.
This is the simplified schema of the tables.
ReportCategory Table
RportCategoryId CategoryName
ApplicationReport Table
ApplicationReportId ReportCategoryId ReportName
Events Table
EventId ReportId CreatedDate
If during the period reports belonging to Inventory gets maximum number of hits and Sales second most then the result should be like
Rank Category ReportName Percentage_Change Trend
1 Inventory Inventory Turn 42% Up
Inventory Inventory Stock 18% Up
2 Sales Discounted Sales 12% Down
Sales Sales return 30% Up
This is what I've come up with
SELECT TOP 10 T1.CategoryName AS Category,T1.ReportName, (ABS(100 * (T1.TotalCategoryHits - T2.TotalCategoryHits)) / T2.TotalCategoryHits)+'%' AS PercentageChange,
CASE When (T1.TotalCategoryHits - T2.TotalCategoryHits) > 0 Then 'Upward'
WHEN (T1.TotalCategoryHits - T2.TotalCategoryHits) < 0 THEN 'Downward'
ELSE 'No Change' END AS Trend
FROM
(SELECT COUNT(c.CategoryId) AS TotalCategoryHits,COUNT(r.ReportCategoryId) Total,c.CategoryName,r.ReportName FROM Event e
inner join Reports r ON r.ReportId = CAST(e.ReportId As INT)
inner join Category c ON r.CategoryId = c.CategoryId
WHERE e.CreatedDate >= #CurrentPeriodStartDate AND e.CreatedBy <= #CurrentPeriodEndDate
GROUP BY c.ReportCategoryName,r.ReportName,e.ReportId) T1
INNER JOIN
(SELECT COUNT(c.CategoryId) AS TotalCategoryHits,COUNT(r.ReportCategoryId) Total,c.CategoryName,r.ReportName FROM Event e
inner join Reports r ON r.ReportId = CAST(e.ReportId As INT)
inner join Category c ON r.CategoryId = c.CategoryId
where e.CreatedDate >= #PrevPeriodStartDate And e.CreatedDate <= #PrevPeriodEndDate
GROUP BY c.ReportCategoryName,r.ReportName,e.ReportId) T2 on T1.ReportId = T2.ReportId
ORDER BY T1.TotalCategoryHits DESC
and I'm nowhere near the desired result.
I want the rows to be ordered by the Category which has gotten the maximum number of hits but when I include ReportName in the Group By to make it part of the result I lose the ordering.And I also dont know how to display the rank.
EDIT: Sql Fiddle
Names of the columns are different from my description here because I have used the schema of original Database.For some unknown reason my query is not working.May be because I have changed some column names and types.But schema is there.

Is this the only way to filter the right table in a left outer join?

I have customer balances stored in their own table. the customer balances table gets a new set of records every day (reflecting the balance that day) but contains balances for other days (yyyy-mm-dd). I wanted to get all UK customers from accountinformation and their balances yesterday from balances. I wanted to include rows from accountinformation even where there is no corresponding record (for yesterday) in balances...
select firstname,lastname,accountnumber,balance from accountinformation i
left outer join balances b
on i.accountnumber = b.account
where country = 'UK' and status = 'OPEN'
and (b.date = '2014-04-10' or b.date is null)
... it did not satisfy the requirement to show rows from accountinformation if there is no corresponding row in balances. I had to write the query like this...
select firstname,lastname,accountnumber,balance from accountinformation i
left outer join (select * from balances where date = '2014-04-10') b
on i.accountnumber = b.account
where country = 'UK' and status = 'OPEN'
.. to get the desired behavour. In the interests of correctness I want to know if there is a more correct way to filter the left table in a left outer join?
you might be able to do
select firstname,lastname,accountnumber,balance from accountinformation i
left outer join balances b
on i.accountnumber = b.account and b.date = '2014-04-10'
where country = 'UK' and status = 'OPEN'

formulating an SSRS query to include zero/empty rows in a GROUP BY

I'm working on a SSRS report and I'm having an issue with my Plant name not showing when there is no data available for the date range selected.
The far left column, first row (technically the 2nd by the image) is where my plant name should appear at all times:
Essentially the first image showed just my blank rows/columns. The first column, first row is where my plant name should be at all times. The remaining columns are my returned data based on date selection.
The second image would show everything working as it should when there is data.
I'm grouping by PlantCode in SSRS which is what gives my my plant name. I don't know how to get the plant name to appear even if there is not data available.
Is this possible?
I THOUGHT I could use something like iif(salesvolume is NOTHING, [PlantCODE],[PlantCode])
Here is the database query for the report
SELECT
PInv.[Plant_Numer],
PInv.[Plant_Code],
PInv.{Department_number],
PInv.[Inventory_Volume],
Pinv.[Inventory_Date], -- 'Last Inventory Date'
pls.[Actual_Volume],
pls.[Budget_Volume],
ppf.[Good_Output_Product_Units] AS 'Production Volume', -- 'Next Day Production
CASE
WHEN coalesce (pls.[Acutal_Volume],0) = 0 and coalesce (pls.[Actual_Sales_Dollars],0) = 0 THEN 0
ELSE ((pls.[Actual_Sales_Dollars/pls.[Actual_Volume])) AS 'Average Price' -- 'Next Day Sales'
FROM
[TrueOpportunity].[dbo].[Production_Fact] pf
inner join [TrueOpportunity].[dbo].[Production_Process_Fact] ppf on ppf.production_number = pf.production_number
inner join [TrueOpportunity].[dbo].[Process] prc on prc.process_number = pf.process_number
inner join [TrueOpportunity].[dbo].[Department] dpt on dpt.department_number = prc.department_number
inner join [WoodProduction_New].[dbo].[Plywood_Layup_Sales] pls on pls.procesS_number = pf.procesS_number
inner join [WoodProduction_New].[dbo].[Process_Inventory] Pinv on PInv.[Inventory_Date] = pf.date
and pls.product_date = pf.date
and dpt.department_number = pinv.department_number
WHERE
pf.date between #BeginningDate and #EndingDate
I think you want to change your query so that Process Inventory is your primary table and all other tables are LEFT JOINED to that table. That way the Plant Number & Code will show up regardless of whether there is matching data in the other tables.
This syntax is probably not completely correct, but I would start out by changing your FROM clause to look something like this:
FROM
[WoodProduction_New].[dbo].[Process_Inventory] Pinv
LEFT JOIN [TrueOpportunity].[dbo].[Production_Fact] pf
ON PInv.[Inventory_Date] = pf.date
LEFT JOIN [TrueOpportunity].[dbo].[Production_Process_Fact] ppf
ON ppf.production_number = pf.production_number
LEFT JOIN [TrueOpportunity].[dbo].[Process] prc
ON prc.process_number = pf.process_number
LEFT JOIN [TrueOpportunity].[dbo].[Department] dpt
ON dpt.department_number = prc.department_number
AND dpt.department_number = pinv.department_number
LEFT JOIN [WoodProduction_New].[dbo].[Plywood_Layup_Sales] pls
ON pls.process_number = pf.process_number
AND pls.product_date = pf.date
Experiment with that and see if you can get it to display the data that you want.

Use SQL to count cases in a certain state at a certain time

I need to develop a query that will count the total number of 'open' cases per month.
I have a 'cases' table with an id and a name, and a 'state_changes' table with a datetime column, a caseid column and a state.
How can I calculate the number of cases in each month that have a record with state 'open' in the past, but without a corresponding record with state closed?
I'm using SQL server 2000.
This should get you close (T-SQL):
SELECT
MONTH(s.casedate) m,
YEAR(s.casedate) y,
COUNT(DISTINCT c.caseid) count_cases
FROM
cases c
INNER JOIN state_changes s ON s.caseid = c.caseid
WHERE
s.state = 'open' /* "with state 'open'" */
AND s.casedate < GETDATE() /* "in the past" */
AND NOT EXISTS ( /* "without corresp. record with state 'closed'" */
SELECT 1 FROM state_changes i WHERE i.caseid = s.caseid AND i.state = 'closed'
)
GROUP BY
MONTH(s.casedate),
YEAR(s.casedate)
EDIT: To make a statistic over all twelve months (independent of actual cases existing in these months) you need a small helper table (let's call it month), that contains nothing but one column (let's call that month as well) with numbers from 1 to 12. Then you join against it:
SELECT
m.month,
COUNT(DISTINCT c.caseid) count_cases
FROM
cases c
INNER JOIN state_changes s ON s.caseid = c.caseid
LEFT JOIN month m ON m.month = MONTH(s.casedate)
WHERE
s.state = 'open'
AND YEAR(c.createddate) = YEAR(GETDATE()) /* whatever */
AND NOT EXISTS (
SELECT 1 FROM state_changes i WHERE i.caseid = s.caseid AND i.state = 'closed'
)
GROUP BY
m.month
ORDER BY
m.month
Create a query of the state changes tables for open events and one for close events.
Create a query that does an outer join of the open to the closed on the case ID returning the case ID from both queries
Query the latter query result for rows where the ID from the "close" event query is null
Count the number of rows in the latter query result.
Something very roughly like (off the top of my head, without correction):
SELECT COUNT (T1.CaseID) FROM (SELECT T1.CaseID AS T1_CaseID, T2.CaseID AS T2_CaseID
FROM ((SELECT CaseID FROM state_changes WHERE state = 'open' AND timestamp BETWEEN 1-Jan-09 AND 30-Jan-09) AS T1 OUTER JOIN (SELECT CaseID FROM state_changes WHERE state = 'closed' AND timestamp BETWEEN 1-Jan-09 AND 30-Jan-09) AS T2 ON T1.CaseID = T2.CaseID)) WHERE T2_CaseID = NULL

Resources