Count how many times you exit route the car - sql

i have a question, im on a project GPS,
When a car on route save in our database a value = 1, but when this go off route save in our database a value=0, so how can i determine the number of times out of route from an SQL query?
This is a example of our table:
Then if you look the pic, value 1 car is on route and 0 car is off route, i want count the values groups, for example my result will be:
Off Route = 2 (times)

There are several ways to do this. Depending on your database, there may be an easier version with window functions such as lead and lag. However, this should be generic with exists:
select count(y1.id)
from yourtable y1
where y1.value = 0 and exists (
select 1
from yourtable y2
where y2.id = y1.id - 1 and y2.value = 1)

;with cteBase as (
Select *,RowNr = Row_Number() over (Order by ID) From YourTable
)
Select A.*
From cteBase A
Join cteBase B on (A.RowNr=B.RowNr+1 and A.Value=0 and B.Value=1)
Returns
ID Value RowNr
4 0 4
6 0 6
I didn't want to assume the ID was incremental so I used Row_Number(). If incremental #sgeddes works just as well

If what you need is just the number of times Value is 1 as on Route and 0 as Off Route, try the following query:
select
sum(Value) as [On_route],
sum(abs(Value-1)) as [Off_Route]
from GPSTable
abs(Value-1) essentially coverts 0 to 1 and 1 to 0 which is easy enough to check,

Related

SQL Server : update multiple rows one by one while incrementing id

I am pretty new to SQL and I thought I was comfortable using it after a while but it still is tough. I am trying to increment ids. I know I could use auto-increment but in this case there are id has relationship with several categories so it has to start with different numbers so I can't do it.
The table looks something like this:
id category
----------------
1000 1
1000 1
...
2000 2
2000 2
...
And I want to make it:
id category
------------------
1000 1
1001 1
1002 1
...
2000 2
2001 2
...
I tried:
UPDATE T1
SET id = CASE
WHEN EXISTS (SELECT id FROM STYLE WHERE T1.id = id)
THEN (SELECT MAX(CAST(id AS INT)) + 1
FROM STYLE
WHERE category = T1.category)
END
FROM STYLE T1
WHERE idStyle = idStyle
But it just added 1 to all rows. How could I go 1 by 1 so it could actually get the incremented max id? Thank you.
In the absense of real sample data, this is a pseudo-sql, however, something like...
UPDATE YT
----SELECT NULL as Ihave no context of other fields in your table
SET id = id + ROW_NUMBER() OVER (PARTITION BY category ORDER BY (SELECT NULL)) - 1
FROM YourTable YT;
You can use row_number() function instead :
select *,
concat(cid, row_number() over (partition by id order by category)-1) as NewId
from style s;

SQL obtaining items ranked by their count(*)

I have been attempting the following query for a while- not sure how to approach this issue I'm having.
I need to obtain bands that cover the second most styles of music - including all equal bands if there is a tie for second. For example for the table band_style,
Band_id | Style
---------------------
1 Rock
2 Pop
1 Punk
3 Classical
1 Metal
2 Rock
4 Pop
4 Rap
The returned result should be
Band_id | Num_styles
2 2
4 2
My initial attempt at a solution:
SELECT band_id, COUNT(*) AS num_styles FROM band_style
GROUP BY band_id HAVING COUNT(*) <
(SELECT MAX(c) FROM
(SELECT COUNT(band_id) AS c
FROM band_style
GROUP BY band_id));
So this gives me the count of all the bands with less styles than the maximum. Now, I'd like to take ALL rows which have the maximum value of this query. I do not want to use rownum or limit because from what I've experienced this doesn't work too well in the case of ties. I am also wondering if there is a way to wrap this in another MAX function, but I don't really see how.
Any help with this issue would be appreciated- also think this would be useful to know to see if it can be applied to 3rd, 4th highest, etc.
(Using Oracle/SQLPlus)
Assuming this is a large data file and we do not necessarily know what the "second highest count" is.
UPDATE: this almost works- gets all bands with less than max number of styles. But calling MAX doesn't seem to be working, as the table returned still has all values of NUM except the max..
WITH data AS (
SELECT band_id, COUNT(*) AS NUM FROM band_style GROUP BY band_id HAVING COUNT (*) <
(SELECT MAX(c) FROM
(SELECT COUNT(band_id) AS c
FROM band_style
GROUP BY band_id)))
SELECT data.band_id, data.NUM FROM data
INNER JOIN ( SELECT band_id m, MAX(NUM) n
FROM data GROUP BY band_id
) t
ON t.band_id = data.band_id
AND t.NUM = data.NUM;
If you have to stick with mysql, this sql will be much more difficult. But if you could switch to mariadb or oracle this should work.
with data as (
select
band_id, count(*) styles,
dense_rank() over (order by count(*) desc) place
from
table1 group by band_id)
select * from data where place=2
http://sqlfiddle.com/#!4/dc3f6/12
Your friend here is the window function dense_rank.
The output is:
BAND_ID STYLES PLACE
2 2 2
4 2 2
And here to avoid some missunderstandings, due to place 2 is here styles 2.
http://sqlfiddle.com/#!4/2be32/3
Now the styles count is different from the place id.
BAND_ID STYLES PLACE
4 3 2
This illustrates that dense_rank does not know the second highest count value beforehand.

How can I create this conditional grouped field on SQL Server 2008?

Sorry for this question, but i cannot resolve this simple query.
I have this table:
ID_Type Item
-----------------
A 1
P 2
P 3
A 4
P 5
A 6
I need to calculate a "group" incremental counter based on ID_Type Field where This field has an "A" Value. This is the expected result:
ID_Type Item Counter
-----------------------------
A 1 1
P 2 1
P 3 1
A 4 2
P 5 2
A 6 3
So every time a record with ID_Type='A' appear, I need to increment the counter. Any help will be apreciated.
In SQL Server 2012+, you can use a cumulative sum:
select t.*,
sum(case when id_type = 'A' then 1 else 0 end) over (order by item) as counter
from t;
This will be much more efficient than a correlated subquery approach, particularly on larger data sets.
One way is a subquery:
SELECT ID_Type, Item, (
SELECT COUNT(*) FROM MyTable t2
WHERE t2.Item <= t1.Item
AND t2.ID_Type='A'
) AS Counter
FROM MyTable t1
ORDER BY Item ASC
This will work on any version of SQL Server.

How to identify subsequent user actions based on prior visits

I want to identify the users who visited section a and then subsequently visited b. Given the following data structure. The table contains 300,000 rows and updates daily with approx. 8,000 rows:
**USERID** **VISITID** **SECTION** Desired Solution--> **Conversion**
1 1 a 0
1 2 a 0
2 1 b 0
2 1 b 0
2 1 b 0
1 3 b 1
Ideally I want a new column that flags the visit to section b. For example on the third visit User 1 visited section b for the first time. I was attempting to do this using a CASE WHEN statement but after many failed attempts I am not sure it is even possible with CASE WHEN and feel that I should take a different approach, I am just not sure what that approach should be. I do also have a date column at my disposal.
Any suggestions on a new way to approach the problem would be appreciated. Thanks!
Correlated sub-queries should be avoided at all cost when working with Redshift. Keep in mind there are no indexes for Redshift so you'd have to rescan and restitch the column data back together for each value in the parent resulting in an O(n^2) operation (in this particular case going from 300 thousand values scanned to 90 billion).
The best approach when you are looking to span a series of rows is to use an analytic function. There are a couple of options depending on how your data is structured but in the simplest case, you could use something like
select case
when section != lag(section) over (partition by userid order by visitid)
then 1
else 0
end
from ...
This assumes that your data for userid 2 increments the visitid as below. If not, you could also order by your timestamp column
**USERID** **VISITID** **SECTION** Desired Solution--> **Conversion**
1 1 a 0
1 2 a 0
2 1 b 0
2 *2* b 0
2 *3* b 0
1 3 b 1
select t.*, case when v.ts is null then 0 else 1 end as conversion
from tbl t
left join (select *
from tbl x
where section = 'b'
and exists (select 1
from tbl y
where y.userid = x.userid
and y.section = 'a'
and y.ts < x.ts)) v
on t.userid = v.userid
and t.visitid = v.visitid
and t.section = v.section
Fiddle:
http://sqlfiddle.com/#!15/5b954/5/0
I added sample timestamp data as that field is necessary to determine whether a comes before b or after b.
To incorporate analytic functions you could use:
(I've also made it so that only the first occurrence of B (after an A) will get flagged with the 1)
select t.*,
case
when v.first_b_after_a is not null
then 1
else 0
end as conversion
from tbl t
left join (select userid, min(ts) as first_b_after_a
from (select t.*,
sum( case when t.section = 'a' then 1 end)
over( partition by userid
order by ts ) as a_sum
from tbl t) x
where section = 'b'
and a_sum is not null
group by userid) v
on t.userid = v.userid
and t.ts = v.first_b_after_a
Fiddle: http://sqlfiddle.com/#!1/fa88f/2/0

Select values in SQL that do not have other corresponding values except those that i search for

I have a table in my database:
Name | Element
1 2
1 3
4 2
4 3
4 5
I need to make a query that for a number of arguments will select the value of Name that has on the right side these and only these values.
E.g.:
arguments are 2 and 3, the query should return only 1 and not 4 (because 4 also has 5). For arguments 2,3,5 it should return 4.
My query looks like this:
SELECT name FROM aggregations WHERE (element=2 and name in (select name from aggregations where element=3))
What do i have to add to this query to make it not return 4?
A simple way to do it:
SELECT name
FROM aggregations
WHERE element IN (2,3)
GROUP BY name
HAVING COUNT(element) = 2
If you want to add more, you'll need to change both the IN (2,3) part and the HAVING part:
SELECT name
FROM aggregations
WHERE element IN (2,3,5)
GROUP BY name
HAVING COUNT(element) = 3
A more robust way would be to check for everything that isn't not in your set:
SELECT name
FROM aggregations
WHERE NOT EXISTS (
SELECT DISTINCT a.element
FROM aggregations a
WHERE a.element NOT IN (2,3,5)
AND a.name = aggregations.name
)
GROUP BY name
HAVING COUNT(element) = 3
It's not very efficient, though.
Create a temporary table, fill it with your values and query like this:
SELECT name
FROM (
SELECT DISTINCT name
FROM aggregations
) n
WHERE NOT EXISTS
(
SELECT 1
FROM (
SELECT element
FROM aggregations aii
WHERE aii.name = n.name
) ai
FULL OUTER JOIN
temptable tt
ON tt.element = ai.element
WHERE ai.element IS NULL OR tt.element IS NULL
)
This is more efficient than using COUNT(*), since it will stop checking a name as soon as it finds the first row that doesn't have a match (either in aggregations or in temptable)
This isn't tested, but usually I would do this with a query in my where clause for a small amount of data. Note that this is not efficient for large record counts.
SELECT ag1.Name FROM aggregations ag1
WHERE ag1.Element IN (2,3)
AND 0 = (select COUNT(ag2.Name)
FROM aggregatsions ag2
WHERE ag1.Name = ag2.Name
AND ag2.Element NOT IN (2,3)
)
GROUP BY ag1.name;
This says "Give me all of the names that have the elements I want, but have no records with elements I don't want"