SQL - SELECT with JOINED Table - sql

I'm trying to do a selection from the user tabel. For each user i would like to sum the Timediff for all posts in tblregtime for current user for given date parameters.
Problem is that i need to get information even if there is no registration done at the given date for current user. If no registration i need output that current user has TotalDiff=0. My current SQL doesn't work in this way. It will just give the fname,lname and TotalDiff if there is a post in tblregtime
sql:
select u.fname,u.lname, sum(cast(TIME_TO_SEC(TIMEDIFF(r.edate,r.sdate)) AS UNSIGNED)-r.break_time) as TotalDiff
from tbluser u
RIGHT OUTER JOIN tblregtime r on r.userid=u.id where r.projectid=21
and year(r.sdate)=2013 and month(r.sdate)=10 and day(r.sdate)=7

If you are trying to keep everything in tbluser, then you want a left join instead of a right join. However, you also need to move the where conditions into the on clause. Otherwise, when there is no match, the comparisons will fail (because the r. values will be NULL):
select u.fname,u.lname,
sum(cast(TIME_TO_SEC(TIMEDIFF(r.edate,r.sdate)) AS UNSIGNED)-r.break_time) as TotalDiff
from tbluser u LEFT JOIN
tblregtime r
on r.userid = u.id and
r.projectid = 21 and
year(r.sdate) = 2013 and month(r.sdate) = 10 and day(r.sdate) = 7;
I would also recommend that you change the final date comparison to something like:
r.sdate = '2013-10-07'
This form would allow the use of an index on r.sdate. As you have written it, the SQL engine (at least the SQL engines I am familiar with) would not be smart enough to use the index.

Related

MS Access SQL Update with Minimum

This SQL is beyond my expertise. I think it should be fairly easy for someone with experience. Here is what I have so far..
SQL is as follows:
UPDATE (Tbl_Stg_Project_Schedule_Dates
INNER JOIN Tbl_Child_ITN ON Tbl_Stg_Project_Schedule_Dates.ms_itn = Tbl_Child_ITN.ITN)
INNER JOIN Tbl_Schedule ON Tbl_Child_ITN.Id = Tbl_Schedule.ID SET Tbl_Schedule.a_construction_start = [Tbl_Stg_Project_Schedule_Dates].[ms_start_date]
WHERE (((Tbl_Stg_Project_Schedule_Dates.ms_tempt_id) In (16,17,18,19,20,21,22,23)));
I want to add one last condition to this being that I only want the minimum of [Tbl_Stg_Project_Schedule_Dates].[ms_start_date] to update the table. I've tried the obvious of wrapping the field in Min, and also tried creating a separate aggregate select statement first (to get the min value with other criteria) that I then tried to create the update query from in new query but no luck.
I believe this is valid Access/Jet SQL. The idea here is to use a subquery to look up the earliest date among all the rows in your subset. I'm not sure if ms_itn was the right column to correlate on but hopefully you get the idea:
UPDATE (Tbl_Stg_Project_Schedule_Dates
INNER JOIN Tbl_Child_ITN ON Tbl_Stg_Project_Schedule_Dates.ms_itn = Tbl_Child_ITN.ITN)
INNER JOIN Tbl_Schedule ON Tbl_Child_ITN.Id = Tbl_Schedule.ID
SET Tbl_Schedule.a_construction_start = [Tbl_Stg_Project_Schedule_Dates].[ms_start_date]
WHERE (((Tbl_Stg_Project_Schedule_Dates.ms_tempt_id) In (16,17,18,19,20,21,22,23)))
and [Tbl_Stg_Project_Schedule_Dates].[ms_start_date] = (
select min(sd.[ms_start_date])
from [Tbl_Stg_Project_Schedule_Dates] as sd
where sd.ms_itn = [Tbl_Stg_Project_Schedule_Dates].ms_itn
)

How to join two queries in access

I am incapable of make a query that return me a results as follows:
TABLES: series, usuarios, siguiendo, valoraciones_personales
Each table has got this records:
example: field1(value), field2(value),...
series (I refer a tv show, I am spanish and here we say "serie=tv_show")
1. id_serie(1),id_titulo('Sons of anarchy')
2. id_serie(2),id_titulo('Lost')
usuarios (user)
1. id_usuario(1), nick('david')
siguiendo (a usser follow a series)
1. id_serie(1),id_usuario(1)
2. id_serie(2),id_usuario(1)
valoraciones_personales (personal assessments)
1. id_serie(1),id_usuario(1),nota(8)
Ok, what I want is a result with all records of the table siguiendo, and if that user valued one of that series, it must shows the score (nota in spanish), and if that user didnĀ“t scored that series, I want to show "without score".
The view I want:
*titulo, nota*
- Sons of anarchy, 8
- Lost, without score
Can anyone help me?
Specifically in MSACCESS
Create a query called something like AllUserSeries
SELECT
U.UserID
,U.FullName
,S.SeriesID
,S.SeriesName
FROM
usuarios as U
,series as S
This is the equivalent of a cross join
Then another:
SELECT
A.FullName
,A.SeriesName
,Nz(Cstr(R.Score),"Not Rated") as Rating
FROM
AllUserSeries AS A
LEFT OUTER JOIN valoraciones_personales AS R
ON A.UserID = R.UserID
AND A.SeriesID = R.SeriesID
WHERE
A.UserID = #UserID
The tricky bit is getting a list of all the series a user may have liked. to do this normally i would do a cross join to get all permutations that could exist, then left join from there to the ratings table using Nz to handle null values as you see fit.
*sorry for kinda making up the other column names it was easier for me to use English hope that okay :D

SQL Query for all systemusers who's organisationunits has a datevalue < ... with pivot table in between

I have 3 tables,
SystemUsers
OrganisationalUnits
UserOrganisationUnits - Pivot with UserId/OrganisationUnitId foreign keys
All tables have an Active bit column and the OrganisationalUnit table has a CoveredTill datetime column.
I want a list of all UserOrganisationUnits where OrganisationalUnit "CoveredTill" is < x (value doesn't matter as will be set in clr) date and all tables records are active.
So far I've only managed to get unique users from my query so any advice would be much appreciated.
SELECT
SystemUsers.DisplayName, SystemUsers.Email,
OrganisationalUnits.Description, OrganisationalUnits.CoveredTill,
OrganisationalUnits.Id, OrganisationalUnits.ParentId, OrganisationalUnits.TypeId
FROM
SystemUsers
RIGHT OUTER JOIN
OrganisationalUnits ON SystemUsers.Id = OrganisationalUnits.Id
INNER JOIN
UserOrganisationUnits ON SystemUsers.Id = UserOrganisationUnits.Id
WHERE
(OrganisationalUnits.Active = 1)
AND (SystemUsers.Active = 1)
AND (UserOrganisationUnits.Active = 1)
AND (OrganisationalUnits.CoveredTill < '2017-03-31 00:00:00.000')
I randomly set the date as the above since I manually set the covered till date as "2015-03-31 00:00:00.000" on all OrganisationalUnits "CoveredTill" columns.
My aim is to have a single sql query string which I can use in my clr stored procedure to create a list of all users who need to be emailed because there organisational unit is no longer covered and for said email to include which organisational unit hence why just having a unique list of users isn't sufficient.
I already have the email code so it's just the query I'm having issues with.
Edit:-
I had first approached this as needing 3 separate queries which I guess after a bit more testing I'll have to revisit unless someone can spot what I'm doing wrong with the above query?
Edit 2:-
After working through the issue with sarin and preparing to provide him a sql db/query copy I found that some of the joins were pointing towards the wrong ID fields (automatically created from different panes so always be wary of anything they create) so below I've included the final solution. (Which includes a couple extra fields I decided might be useful).
SELECT OrganisationalUnits.Description, OrganisationalUnits.CoveredTill, SystemUsers.DisplayName, SystemUsers.Email,
UserOrganisationUnits.LastVisited AS UOULastVisited, SystemUsers.LastVisited AS SULastVisited
FROM UserOrganisationUnits INNER JOIN
OrganisationalUnits ON UserOrganisationUnits.OrganisationUnitId = OrganisationalUnits.Id INNER JOIN
SystemUsers ON UserOrganisationUnits.UserId = SystemUsers.Id
WHERE (OrganisationalUnits.Active = 1) AND (SystemUsers.Active = 1) AND (UserOrganisationUnits.Active = 1) AND
(OrganisationalUnits.CoveredTill < '2017-03-31 00:00:00.000')
Performing a Right\left join before you do an inner join can give you unexpected results as it may produce nulls and your inner join will then attempt to filter them out. I'm not entirely clear about the relationships between the tables but can you turn them all into INNER JOINS to get the same list and produce you a similar result based on your expected outcome?
"My aim is.... to create a list of all users who need to be emailed because there organisational unit is no longer covered"
UPDATED:
SELECT
SystemUsers.DisplayName, SystemUsers.Email,
OrganisationalUnits.Description, OrganisationalUnits.CoveredTill,
OrganisationalUnits.Id, OrganisationalUnits.ParentId, OrganisationalUnits.TypeId
FROM
UserOrganisationUnits
INNER JOIN SystemUsers ON SystemUsers.Id = UserOrganisationUnits.Id
AND (SystemUsers.Active = 1)
INNER JOIN OrganisationalUnits ON OrganisationalUnits.Id = SystemUsers.Id
AND OrganisationalUnits.Active = 1
AND OrganisationalUnits.CoveredTill < '2017-03-31 00:00:00.000'
WHERE UserOrganisationUnits.Active = 1

SQL Inner Join and Grouping

It's been a while since I've done SQL, but I have a rather pressing issue.
My db-layout is as following:
Now, starting from a Users.ID, I want to get all the Rounds the user has played. The user could be Hosts.HostID, Host.GuestID, or even both. Where he is both, it should not show op in the results.
The results I need from the Query are the Hosts.Name and all the fields of Rounds. In general what I want to do is display a list of all the Hosts (actually these are the Games) in which the user has participated, as a Host or as a Guest, along with perhaps a total score. When clicking on this, some dropdown will appear showing the individual round scores, words, ...
Now I was wondering whether this was possible in a single query. Of course I could do a query getting all the Hosts and then per Host a query for each Round, but that doesn't seem that performant. This is what I've come up with so far:
SELECT Rounds.ID, Rounds.GameID, Rounds.Round, Rounds.Score, Rounds.Word
, Hosts.ID, Hosts.HostID, Hosts.GuestID
FROM Rounds INNER JOIN Hosts
ON Rounds.GameID = Hosts.ID
INNER JOIN Users
ON Hosts.hostID = Users.ID
WHERE Users.ID = 5
The issue is however that it doesn't filter out where the user is both host AND guest, and I can't seem to Group it by Hosts.ID either.
Add Hosts.hostID <> Hosts.guestID to the where clause.
If you are using SQL Server 2005 or later version, you could modify your present query like this:
SELECT Rounds.ID, Rounds.GameID, Rounds.Round, Rounds.Score, Rounds.Word
, Hosts.ID, Hosts.HostID, Hosts.GuestID
FROM Rounds INNER JOIN Hosts
ON Rounds.GameID = Hosts.ID
CROSS APPLY
(
SELECT Hosts.HostID
UNION
SELECT Hosts.GuestID
) AS u (UserID)
WHERE u.UserID = 5
;
The CROSS APPLY clause would produce either one or two rows, depending on whether HostID and GuestID are equal. In either event, the WHERE condition would ultimately leave at most one. Thus, the above query would give you only the games (with all their rounds) where the specified user participated.
And from that query you could easily get to something like this:
SELECT Hosts.ID,
TotalScore = SUM(Rounds.Score)
FROM
...
GROUP BY
Hosts.ID
;

MySQL to return only last date / time record

We have a database that stores vehicle's gps position, date, time, vehicle identification, lat, long, speed, etc., every minute.
The following select pulls each vehicle position and info, but the problem is that returns the first record, and I need the last record (current position), based on date (datagps.Fecha) and time (datagps.Hora). This is the select:
SELECT configgps.Fichagps,
datacar.Ficha,
groups.Nombre,
datagps.Hora,
datagps.Fecha,
datagps.Velocidad,
datagps.Status,
datagps.Calleune,
datagps.Calletowo,
datagps.Temp,
datagps.Longitud,
datagps.Latitud,
datagps.Evento,
datagps.Direccion,
datagps.Provincia
FROM asigvehiculos
INNER JOIN datacar ON (asigvehiculos.Iddatacar = datacar.Id)
INNER JOIN configgps ON (datacar.Configgps = configgps.Id)
INNER JOIN clientdata ON (asigvehiculos.Idgroup = clientdata.group)
INNER JOIN groups ON (clientdata.group = groups.Id)
INNER JOIN datagps ON (configgps.Fichagps = datagps.Fichagps)
Group by Fichagps;
I need same result I'm getting, but instead of the older record I need the most recent
(LAST datagps.Fecha / datagps.Hora).
How can I accomplish this?
Add ORDER BY datagps.Fecha DESC, datagps.Hora DESC LIMIT 1 to your query.
I'm not sure why you are having any problems with this as Lex's answers seem good.
I would start putting ORDER BY's in your query so it puts them in an order, when it's showing the record you want as the first one in the list, then add the LIMIT.
If you want the most recent, then the following should be good enough:
ORDER BY datagps.Fecha DESC, datagps.Hora DESC
If you simply want the record that was added to the database most recently (irregardless of the date/time fields), then you could (assuming you have an auto-incremental primary key in the datagps table (I assume it's called dataID for this example)):
ORDER BY datagps.dataID DESC
If these aren't showing the data you want - then there is something missing from your example (maybe data-types aren't DATETIME fields? - if not - then maybe a CONVERT to change them from their current type before ORDERing BY would be a good idea)
EDIT:
I've seen the screenshot and I'm confused as to what the issue is still. That appears to be showing everything in order. Are you implying that there are many more than 5 records? How many are you expecting?
Do you mean: for each record returned, you want the one row from the table datagps with the latest date and time attached to the result? If so, how about this:
# To show how the query will be executed
# comment to return actual results
EXPLAIN
SELECT
configgps.Fichagps, datacar.Ficha, groups.Nombre, datagps.Hora, datagps.Fecha,
datagps.Velocidad, datagps.Status, datagps.Calleune, datagps.Calletowo,
datagps.Temp, datagps.Longitud, datagps.Latitud, datagps.Evento,
datagps.Direccion, datagps.Provincia
FROM asigvehiculos
INNER JOIN datacar ON (asigvehiculos.Iddatacar = datacar.Id)
INNER JOIN configgps ON (datacar.Configgps = configgps.Id)
INNER JOIN clientdata ON (asigvehiculos.Idgroup = clientdata.group)
INNER JOIN groups ON (clientdata.group = groups.Id)
INNER JOIN datagps ON (configgps.Fichagps = datagps.Fichagps)
########### Add this section
LEFT JOIN datagps b ON (
configgps.Fichagps = b.Fichagps
# wrong condition
#AND datagps.Hora < b.Hora
#AND datagps.Fecha < b.Fecha)
# might prevent indexes to be used
AND (datagps.Fecha < b.Fecha OR (datagps.Fecha = b.Fecha AND datagps.Hora < b.Hora))
WHERE b.Fichagps IS NULL
###########
Group by configgps.Fichagps;
Similar question here only that that one uses outer joins.
Edit (again):
The conditions are wrong so corrected it. Can you show us the output of the above EXPLAIN query so we can pinpoint where the bottle neck is?
As hurikhan77 said, it will be better if you could convert both of the the columns into a single datetime field - though I'm guessing this would not be possible for your case (since your database is already being used?)
Though if you can convert it, the condition (on the join) would become:
AND datagps.FechaHora < b.FechaHora
After that, add an index for datagps.FechaHora and the query would be fast(er).
What you probably want is getting the maximum of (Fecha,Hora) per grouped dataset? This is a little complicated to accomplish with your column types. You should combine Fecha and Hora into one column of type DATETIME. Then it's easy to just SELECT MAX(FechaHora) ... GROUP BY Fichagps.
It could have helped if you posted your table structure to understand the problem.