Laravel Postgres sql. Make a subquery via the Eloquent - laravel-8

I have a two relations:
vehicle, track.
by $vehicle->tracks() - i can get all tracks from $vehicle model
And i have to make a postgres query:
select x.* from (select *, extract(epoch from tracked_at - lag(tracked_at, 1) over ( ORDER BY tracked_at)) as diff from tracks)
as x where x.diff >= 30
via the eloquent
Please help me
i have tried to do it like this:
$test = $tracks
->select(DB::raw('*, extract(epoch from tracked_at - lag(tracked_at, 1) over ( ORDER BY tracked_at)) as diff'));

Related

How to get row count on multiple schemas with same table name

There are many schemas and each schema has users table. I use different schemas for each organization/tenant. I want to get users count with COUNT function. How can I do that on all schemas?
i.e
schamas = [org1,org2,org3]
org1.tables = (users,videos,pictures)
org2.tables = (users,videos,pictures)
org3.tables = (users,videos,pictures)
What I tried
select count(users) from *.users;
Use scalar sub-queries. Using union CTEs for users, videos and pictures or PL/pgSQL are possible alternatives. Please note that the query below - not a very smart one indeed nor very efficient - has a regular structure and could be built mechanically by using a template.
select
(select count(*) from org1.users) +
(select count(*) from org2.users) +
(select count(*) from org3.users) as userscnt,
(select count(*) from org1.videos) +
(select count(*) from org2.videos) +
(select count(*) from org3.videos) as videoscnt,
(select count(*) from org1.pictures) +
(select count(*) from org2.pictures) +
(select count(*) from org3.pictures) as picturescnt;

Find duplicates within a given date range

I'm trying to find a query which will return all entries in the database set that has duplicate signum(identifiers) and who are inside a given date range.
I tried something like this:
select * from dmt_teamresource where dmt_teamresource.signum IN (
select dmt_teamresource.signum from dmt_teamresource group by dmt_teamresource.signum having count((dmt_teamresource.signum)) > 1)
which returns all entries from dmt_teamresource that are duplicates. Now I need to add the date range somehow. Was thinking of something like:
where dmt_teamresource.startdate is between '2021-01-31' and '2021-12-31'
But don't know how to combine the queries.
Use window functions:
select tr.*
from (select ts.*, count(*) over (partition by tr.signum) as cnt
from dmt_teamresource tr
where . . . -- whatever conditions you want here
) tr
where cnt >= 2;

how to select the latest data?

I am using laravel 5, and I have a question for the SQL
there is the table that record the function change of employee.
I just need the latest function so I wonder to use the "group by".
but even I get the latest date, I could not get the corresponding data .
The most close to what I want is like this
DB::table('function_history')
->select(DB::raw('id_history,function_history.CUID,
max(function_change_date)as time, id_function'))
->orderBy('time','desc') ->groupBy('CUID')->get();
Thanks for your help
Ok. You just need to know the rows that corresponds to rows with max date.
Pure sql query:
select
id_history, CUID, id_function, function_change_date
from
function_history t1
join
(select max(function_change_date) as maxdt from function_history group by CUID) t2 on t1.function_change_date = t2.maxdt
Laravel query:
DB::table('function_history')->select('id_history', 'CUID', 'id_function', 'function_change_date')
->join(DB::raw('(select max(function_change_date) as maxdt from function_history group by CUID) temp'),
'temp.maxdt', '=', 'date'
)->get();
You can select all employees with their current function with this SQL query:
SELECT
function_history.*
FROM
function_history,
(
SELECT
MAX(function_change_date) as last_change_date,
CUID
FROM
function_history
GROUP BY
CUID
ORDER BY function_change_date DESC
) tmp
WHERE
function_history.CUID = tmp.CUID
AND
function_history.function_change_date = tmp.last_change_date
I’m not sure why you’re building a raw SQL query, and grouping. If you just want the latest function changes for a particular CUID, then select data and order by the date:
DB::table('function_history')
->where('CUID', '=', $cuid)
->orderBy('function_change_date')
->get();
Please use below query and convert into laravel format
SELECT
`CUID`,
MAX(`function_change_date`) AS timechange,
(SELECT
`id_function`
FROM
`function_history` fc
WHERE fc.function_change_date = MAX(f.`function_change_date`)) AS id_function
FROM
`function_history` f
GROUP BY CUID ;

Selecting a grouped condition in an Aggregate query

I have a view that averages some statistics by averaging past rows relative
to the current outer row. Think of a batting average for each previous at bat for each batter. This works as I would like but, I would like more control over the old_foo.dates
The views idealized query is like this:
create view myview as
select
avg(old_foo.stuff),
foo.person_id,
foo.date_ as this_date
from
foo,
join ( select stuff, person_id, date_ from foo) as old_foo
on old_foo.date_ < foo.date_
group by person_id, this_date
;
But what I would really like is to be able set the minimum old_foo.date from the
view so I could be able to create arbitrary moving averages on the fly.
Such as:
select * from myview where mindate > now()::date - 10
(mindate is fictitious since I lose it with the group by)
I know I can do this with a function but I would prefer not too. Would CTE's give me more flexibility with what I want?
edit
I can't bring the oldate column to the top level of the view without grouping it (which is not what I want.) I want the view to be general so I could just as easily do a 10 day moving average as a 20 day one, or any date I would like. The olddates in the inner query so I have no access to it once I create a view.
I figured it out :)
create view myview as
select
avg(old_foo.stuff),
foo.person_id,
foo.date_ as this_date,
offset
from
generate_series(1, 100) as offset,
foo,
join ( select stuff, person_id, date_ from foo) as old_foo
on old_foo.date_ < foo.date_
and old_foo.date_ > foo.date_ - offset
group by person_id, this_date, offset
;
select * from myview where offset = 10;
Then offset would simulate a function parameter.
Try using the having clause here is some reference
http://www.postgresql.org/docs/8.1/static/tutorial-agg.html
I believe it would look something like this.
create view myview as
select
avg(old_foo.stuff),
foo.person_id,
foo.date_ as this_date
from
foo,
join ( select stuff, person_id, date_ from foo) as old_foo
on old_foo.date_ < foo.date_
group by person_id
having min(foo.date_) <= now() - 10

SQL Return Random Numbers Not In Table

I have a table with user_ids that we've gathered from a streaming datasource of active accounts. Now I'm looking to go through and fill in the information about the user_ids that don't do much of anything.
Is there a SQL (postgres if it matters) way to have a query return random numbers not present in the table?
Eg something like this:
SELECT RANDOM(count, lower_bound, upper_bound) as new_id
WHERE new_id NOT IN (SELECT user_id FROM user_table) AS user_id_table
Possible, or would it be best to generate a bunch of random numbers with a scripted wrapper and pass those into the DB to figure out non existant ones?
It is posible. If you want the IDs to be integers, try:
SELECT trunc((random() * (upper_bound - lower_bound)) + lower_bound) AS new_id
FROM generate_series(1,upper_bound)
WHERE new_id NOT IN (
SELECT user_id
FROM user_table)
You can wrap the query above in a subselect, i.e.
SELECT * FROM (SELECT trunc(random() * (upper - lower) + lower) AS new_id
FROM generate_series(1, count)) AS x
WHERE x.new_id NOT IN (SELECT user_id FROM user_table)
I suspect you want a random sampling. I would do something like:
SELECT s
FROM generate_series(1, (select max(user_id) from users) s
LEFT JOIN users ON s.s = user_id
WHERE user_id IS NULL
order by random() limit 5;
I haven't tested this but the idea should work. If you have a lot of users and not a lot of missing id's it will perform better than the other options, but performance no matter what you do may be a problem.
My pragmatic approach would be: generate 500 random numbers and then pick one which is not in the table:
WITH fivehundredrandoms AS ( RANDOM(count, lower_bound, upper_bound) AS onerandom
FROM (SELECT generate_series(1,500)) AS fivehundred )
SELECT onerandom FROM fivehundredrandoms
WHERE onerandom NOT IN (SELECT user_id FROM user_table WHERE user_id > 0) LIMIT 1;
There is way to do what you want with recursive queries, alas it is not nice.
Suppose that you have the following table:
CREATE TABLE test (a int)
To simplify, you want to insert random numbers from 0 to 4 (random() * 5)::int that are not in the table.
WITH RECURSIVE rand (i, r, is_new) AS (
SELECT
0,
null,
false
UNION ALL
SELECT
i + 1,
next_number.v,
NOT EXISTS (SELECT 1 FROM test WHERE test.a = next_number.v)
FROM
rand r,
(VALUES ((random() * 5)::int)) next_number(v)
-- safety check to make sure we do not go into an infinite loop
WHERE i < 500
)
SELECT * FROM rand WHERE rand.is_new LIMIT 1
I'm not super sure, but PostgreSQL should be able to stop iterating once it has one result, since it knows that the query has limit 1.
Nice thing about this query is that you can replace (random() * 5)::int with any id generating function that you want