Query to get only cars inside car park - sql

I want to do a report from a car park, and with this car park get the cars that are inside at the moment when i call the query.
Supose that we have two entrances and two exits, how i can do in SQL to get only cars inside that parking.
How I can get this values?
Example of records of my table:
id lic plate date lane access id_user
__________________________________________________________________
10 1234-BK 2020-08-11 12:24:00.000 1 OK 4
11 1234-BK 2020-08-11 12:25:00.000 3 OK 4
With that example we supose that this car is out of the car park because lane 1 is from entrances and lane 3 is from exit, so the last record we have is from a exit lane.
With this information could you orientate me to do this query that get all the cars inside?

You should be storing whether cars are entering or leaving. That seems pretty basic for such an application.
If you don't have that, you can count the number of records up to a given time and if the value is odd the car is in and even it is out. So to get the cars inside:
select lic_plate
from t
where date < #date
group by lic_plate
having count(*) % 2 = 1;
If you have entrance and exit lanes, you can get the last record using a correlated subquery and check for the final lance:
select t.*
from t
where t.date = (select max(t2.date)
from t t2
where t2.lic_plate = t.lic_plate and
t2.date < #date
) and
t.lane = 1; -- last lane is an entrance lane

To get rows where the last 'lane' for each 'lic_plate' is lane 1 the OP could use a windowing function.
;with get_max_cte as (
select t.*, ROW_NUMBER() over (partition by lic_plate order by t.[date] desc) rn)
select * from get_max_cte
where rn=1 and lane=1

Related

How to consecutively count everything greater than or equal to itself in SQL?

Let's say if I have a table that contains Equipment IDs of equipments for each Equipment Type and Equipment Age, how can I do a Count Distinct of Equipment IDs that have at least that Equipment Age.
For example, let's say this is all the data we have:
equipment_type
equipment_id
equipment_age
Screwdriver
A123
1
Screwdriver
A234
2
Screwdriver
A345
2
Screwdriver
A456
2
Screwdriver
A567
3
I would like the output to be:
equipment_type
equipment_age
count_of_equipment_at_least_this_age
Screwdriver
1
5
Screwdriver
2
4
Screwdriver
3
1
Reason is there are 5 screwdrivers that are at least 1 day old, 4 screwdrivers at least 2 days old and only 1 screwdriver at least 3 days old.
So far I was only able to do count of equipments that falls within each equipment_age (like this query shown below), but not "at least that equipment_age".
SELECT
equipment_type,
equipment_age,
COUNT(DISTINCT equipment_id) as count_of_equipments
FROM equipment_table
GROUP BY 1, 2
Consider below join-less solution
select distinct
equipment_type,
equipment_age,
count(*) over equipment_at_least_this_age as count_of_equipment_at_least_this_age
from equipment_table
window equipment_at_least_this_age as (
partition by equipment_type
order by equipment_age
range between current row and unbounded following
)
if applied to sample data in your question - output is
Use a self join approach:
SELECT
e1.equipment_type,
e1.equipment_age,
COUNT(*) AS count_of_equipments
FROM equipment_table e1
INNER JOIN equipment_table e2
ON e2.equipment_type = e1.equipment_type AND
e2.equipment_age >= e1.equipment_age
GROUP BY 1, 2
ORDER BY 1, 2;
GROUP BY restricts the scope of COUNT to the rows in the group, i.e. it will not let you reach other rows (rows with equipment_age greater than that of the current group). So you need a subquery or windowing functions to get those. One way:
SELECT
equipment_type,
equipment_age,
(Select COUNT(*)
from equipment_table cnt
where cnt.equipment_type = a.equipment_type
AND cnt.equipment_age >= a.equipment_age
) as count_of_equipments
FROM equipment_table a
GROUP BY 1, 2, 3
I am not sure if your environment supports this syntax, though. If not, let us know we will find another way.

Adding same random rows to table in SQL

I have the following base table:
_ID_ _Name_
1 Bart Smit
2 Ahmed Lissabon
3 Medina Aziz
4 Ben Joeson
Whereby I would like to assign random titles to above table. However, the same titles should be assigned to the same persons every time the query is run. Thus if I have the following table:
_Titles_
Captain
Mr.
Ms.
Prince
King
Queen
Lieutenant
Doctor
Sir
So the output could look like this:
_ID_ _Title_ _Name_
1 Doctor Bart Smit
2 King Ahmed Lissabon
3 Captain Medina Aziz
4 Sir Ben Joeson
But then it should assign those titles to those names every time I run the code. Now I use the NEWID() in combination with a CROSS APPLY and it randomly assigns titles to names every time I run it.
SELECT _ID_, R._Title_, _NAME_
FROM TABLE
CROSS APPLY
(
SELECT TOP 1 Title
FROM #titles
WHERE TABLE.[_ID_]= TABLE.[_ID_]
ORDER BY NEWID()
) R
If you want the same result every time instead of just running query, update table:
WITH cte AS (
SELECT t.*, R.title AS new_title
FROM TABLE t
CROSS APPLY(SELECT TOP 1 Title
FROM #titles
ORDER BY NEWID()) R
WHERE _TITLE_ IS NULL
)
UPDATE cte
SET _Title_ = new_title;
You can't use any random calculation if it has to be repeatable (unless you add a new column to store that random value).
This applies checksum and modulo to get a repeatable value:
select *
from tab
join
( select *
,row_number() over (order by title) -1 as rn
from titles
) as t
-- or simply hardcoded 9
on abs(checksum(tab.Name,tab.id) % (select count(*) from titles)) = t.rn
;
Of course this will still return different when the number of titles changes (or add an ID column to titles).

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.

Selecting n-th to last values

I have a table like so:
id device group
-----------------
1 a 1000
2 a 1000
3 b 1001
4 b 1001
5 b 1001
6 b 1002
8 a 1003
9 a 1003
10 a 1003
11 a 1003
12 b 1004
13 b 1004
All id's and groups are sequential. What I would like is to select id and device based on groups and devices. Think of it as a pagination type selection. Getting the last group is a simple inner selection, but how do I select the second last group, or the third last group - etc.
I tried the row number function like this:
SELECT * FROM
( SELECT *, ROW_NUMBER() OVER (PARTITION BY device ORDER BY group DESC) rn FROM data) tmp
WHERE rn = 1;
.. but changing rn is giving me the previous id, not the previous group.
I would like to end up with a selection that could accomodate these results:
device = a, group = latest:
id device group
10 a 1003
11 a 1003
device = a, group = latest - 1:
id device group
1 a 1000
2 a 1000
Any one know how to accomplish this?
Edit:
Use case is a GPS enabled device in a car, sending data every 30 seconds. Imagine going on a drive today. First you go to the shops, then you go home. the first trip is you driving to the shop. The second trip is you driving back. I want to show those trips on a map, but it means I need to identify your last trip, and then the trip before it - ad infinitum, until you run out of trips.
You can try this approach:
`with x as (
select distinct page
from test_table),
y as (
select x.page
,row_number() over (order by page desc) as row_num
from x)
select test_table.* from test_table join y on y.page = test_table.page
where y.row_num =2`
I will try to explain what I have did here.
The first block(x) returns the distinct groups(pages in my case).
The second block(y) assigns row numbers to the groups in terms of their rank. In this case the ranking is in descending order of the pages.
Finally the third block, selects the desired value for the desired page. In case you want the pen-ultimate page , type rouw_num=2, if third from last use row_num =3 and likewise.
You can play around with the values [here]: http://sqlfiddle.com/#!15/190c06/26
Use dense_rank():
select d.*
from (select d.*, dense_rank() over (order by group_id desc) as seqnum
from data d
where device = 'a'
) d
where seqnum = 2;

Postgresql : Check if the last number is the highest

I have large database and one field should be an incremental number, but it sometimes resets and I must detect them (the bold rows)
Table 1:
Shop #Sell DATE
EC1 56 1/10/2015
EC1 57 2/10/2015
**EC1 11 3/10/2015
EC1 12 4/10/2015**
AS2 20 1/10/2015
AS2 21 2/10/2015
AS2 22 3/10/2015
AS2 23 4/10/2015
To solve this problem I thought to find the highest number of each SHOP and check if it is the number with the highest DATE. Do you know another easier way to do it?
My concern is that it can be a problem to do the way I am thinking since I have a large database.
Do you know how I can do the query I am thinking of or do you have any others ideas?
The query you have in mind will give you all Shop values having a discontinuity in Sell number.
If you want to get the offending record you can use the following query:
SELECT Shop, Sell, DATE
FROM (
SELECT Shop, Sell, DATE,
LAG(Sell) OVER (PARTITION BY Shop ORDER BY DATE) AS prevSell
FROM Shops ) t
WHERE Sell < prevSell
ORDER BY DATE
LIMIT 1
The above query will return the first discontinuity found within each Shop partition.
Output:
Shop Sell DATE
---------------------
EC1 11 2015-03-10
Demo here
EDIT:
In case you cannot use windowed function and you only want the id of the shop having the discontinuity, then you can use the following query:
SELECT s.Shop
FROM Shops AS s
INNER JOIN (
SELECT Shop, MAX(Sell) AS Sell, MAX(DATE) AS DATE
FROM Shops
GROUP BY Shop ) t
ON s.Shop = t.Shop AND s.DATE = t.DATE
WHERE t.Sell <> s.Sell
The above will work provided that you have unique DATE values per Shop.
I think the following is the type of query you want:
select s.*
from (select shop, max(sell) as maxsell,
first_value(sell) over (partition by shop order by date desc) as lastsell
from shops s
group by shop
) s
where maxsell <> lastsell;