So I have two tables:
workersTbl (workerName and workerID)
workersLogTbl (workerID and workerLogTime)
e.g. workerTbl: workerName = John Smith | workerID = 5
When you pull the workerLogTbl (i.e.
SELECT * FROM workerLogTbl where workerID = 5
This should give you multiple times the worker logged in.
How do I pull all the workers names and also pull the latest log in time (supposing I join the two tables using workerID)?
Thanks.
you can try:
select l.*
from workerlogtbl l
inner join workerstbl w on l.workerid = w.workerid
where l.workerid = 5
order by l.workerlogtime desc
what i have done is join the two tables together with the common fields from both tables and then ordered by the workerlogtime in descending order. if you need more info on joins take a look here
What have you tried so far? Try adding a GROUP BY w.workerId clause and adding aggregate function in the SELECT list, MAX(t.workerLogTime). –
SELECT w.workerId
, MAX(t.workerLogTime) AS latest_time
FROM worker w
LEFT
JOIN workerLogTbl t
ON t.workerId = w.workerId
GROUP BY w.workerId
ORDER BY w.worderId
The first way is to use an inner join, which assumes every worker you are interested in has an entry in the workersLogTbl
SELECT w.workerName, max(wl.workerLogTime) as LastLogTime
FROM workersTbl w
INNER JOIN workersLogTbl tl on (w.workerId = wl.workerId)
WHERE (w.workerId = 5)
GROUP BY w.workerName
A second way uses an outer join. This is useful to capture those workers who do not have an entry in the workersLogTbl. Note that if there is no corresponding entry in workersLogTbl then LastLogTime will be null.
SELECT w.workerName, max(wl.workerLogTime) as LastLogTime
FROM workersTbl w
LEFT OUTER JOIN workersLogTbl tl on (w.workerId = wl.workerId)
WHERE (w.workerId = 5)
GROUP BY w.workerName
note that the outer word isn't generally necessary: left join is normally sufficient.
A third way is to use a subquery. This is useful if you are also wanting to get other aggregate results in the same query. Such as if we wanted the first and last log time for the worker, for example:
SELECT w.workerName,
(SELECT MAX(wl.workerLogTime) FROM workersLogTbl wl WHERE wl.workerId = w.workerId) as LastLogTime,
(SELECT MIN(wl.workerLogTime) FROM workersLogTbl wl WHERE wl.workerId = w.workerId) as FirstLogTime
FROM workersTbl w
WHERE (w.workerId = 5)
side note:
Don't name your tables with Tbl on the end. It's obvious that is a table and doesn't need the suffix and is simply noise. Also, use regular capitalization for the column names. ie: WorkerId instead of workerId
Related
How I can add two fields that belong to an inner join?
I have this code:
select
SUM(ACT.NumberOfPlants ) AS NumberOfPlants,
SUM(ACT.NumOfJornales) AS NumberOfJornals
FROM dbo.AGRMastPlanPerformance MPR (NOLOCK)
INNER JOIN GENRegion GR ON (GR.intGENRegionKey = MPR.intGENRegionLink )
INNER JOIN AGRDetPlanPerformance DPR (NOLOCK) ON
(DPR.intAGRMastPlanPerformanceLink =
MPR.intAGRMastPlanPerformanceKey)
INNER JOIN vwGENPredios P (NOLOCK) ON ( DPR.intGENPredioLink =
P.intGENPredioKey )
INNER JOIN AGRSubActivity SA (NOLOCK) ON (SA.intAGRSubActivityKey =
DPR.intAGRSubActivityLink)
LEFT JOIN (SELECT RA.intGENPredioLink, AR.intAGRActividadLink,
AR.intAGRSubActividadLink, SUM(AR.decNoPlantas) AS
intPlantasTrabajads, SUM(AR.decNoPersonas) AS NumOfJornales,
SUM(AR.decNoPlants) AS NumberOfPlants
FROM AGRRecordActivity RA WITH (NOLOCK)
INNER JOIN AGRActividadRealizada AR WITH (NOLOCK) ON
(AR.intAGRRegistroActividadLink = RA.intAGRRegistroActividadKey AND
AR.bitActivo = 1)
INNER JOIN AGRSubActividad SA (NOLOCK) ON (SA.intAGRSubActividadKey
= AR.intAGRSubActividadLink AND SA.bitEnabled = 1)
WHERE RA.bitActive = 1 AND
AR.bitActive = 1 AND
RA.intAGRTractorsCrewsLink IN(2)
GROUP BY RA.intGENPredioLink,
AR.decNoPersons,
AR.decNoPlants,
AR.intAGRAActivityLink,
AR.intAGRSubActividadLink) ACT ON (ACT.intGENPredioLink IN(
DPR.intGENPredioLink) AND
ACT.intAGRAActivityLink IN( DPR.intAGRAActivityLink) AND
ACT.intAGRSubActivityLink IN( DPR.intAGRSubActivityLink))
WHERE
MPR.intAGRMastPlanPerformanceKey IN(4) AND
DPR.intAGRSubActivityLink IN( 1153)
GROUP BY
P.vchRegion,
ACT.NumberOfFloors,
ACT.NumOfJournals
ORDER BY ACT.NumberOfFloors DESC
However, it does not perform the complete sum. It only retrieves all the values of the columns and adds them 1 by 1, instead of doing the complete sum of the whole column.
For example, the query returns these results:
What I expect is the final sums. In NumberOfPlants the result of the sum would be 163,237 and of NumberJornales would be 61.
How can I do this?
First of all the (nolock) hints are probably not accomplishing the benefit you hope for. It's not an automatic "go faster" option, and if such an option existed you can be sure it would be already enabled. It can help in some situations, but the way it works allows the possibility of reading stale data, and the situations where it's likely to make any improvement are the same situations where risk for stale data is the highest.
That out of the way, with that much code in the question we're better served with a general explanation and solution for you to adapt.
The issue here is GROUP BY. When you use a GROUP BY in SQL, you're telling the database you want to see separate results per group for any aggregate functions like SUM() (and COUNT(), AVG(), MAX(), etc).
So if you have this:
SELECT Sum(ColumnB) As SumB
FROM [Table]
GROUP BY ColumnA
You will get a separate row per ColumnA group, even though it's not in the SELECT list.
If you don't really care about that, you can do one of two things:
Remove the GROUP BY If there are no grouped columns in the SELECT list, the GROUP BY clause is probably not accomplishing anything important.
Nest the query
If option 1 is somehow not possible (say, the original is actually a view) you could do this:
SELECT SUM(SumB)
FROM (
SELECT Sum(ColumnB) As SumB
FROM [Table]
GROUP BY ColumnA
) t
Note in both cases any JOIN is irrelevant to the issue.
I need some help. I've got a list of customers and services that they've used, but I need to narrow that list to customers that have used more than one service (excluding those who've only used one service). They have sometimes used the same service more than once, but I need a list of unique services.
The below brings back the main list of customers.
SELECT
DISTINCT M.CustID
,S.ServiceID
,R.ReceivedDate
,S.ServiceRequestID
FROM Customers AS M
LEFT OUTER JOIN CustomerDates AS R ON M.CustID = R.CustID
LEFT OUTER JOIN Service1 AS S ON R.ServiceRequestID = S.ServiceRequestID
WHERE S.CloseDate IS NULL
What I need is a list that excludes the first three lines as they have only used one service, whereas the next seven I need as they've used more than one service.
It is quite likely that you can just use NOT EXISTS. This is likely to do what you want:
SELECT M.CustID, S.ServiceID, R.ReceivedDate, S.ServiceRequestID
FROM Customers M JOIN
CustomerDates R
ON M.CustID = R.CustID JOIN
Service1 S
ON R.ServiceRequestID = S.ServiceRequestID
WHERE S.CloseDate IS NULL AND
EXISTS (SELECT 1
FROM CustomerDates cd2
WHERE cd2.CustId = m.custId AND cd2.ServiceRequestID <> r.ServiceRequestID
);
I'm not 100% sure this is equivalent to your query. The SELECT DISTINCT should not be needed, unless you have duplicates in your tables. Also, you seem to require matches between the tables, so LEFT JOIN is not appropriate. It it is not clear if the WHERE condition is relevant for finding duplicates.
Try the following, it should work
SELECT
CustID
,ServiceID
,ReceivedDate
,ServiceRequestID
FROM
(SELECT
DISTINCT M.CustID
,S.ServiceID
,R.ReceivedDate
,S.ServiceRequestID
,count(*) over (partition by M.CustID) as total
FROM Customers AS M
LEFT OUTER JOIN CustomerDates AS R ON M.CustID = R.CustID
LEFT OUTER JOIN Service1 AS S ON R.ServiceRequestID = S.ServiceRequestID
WHERE S.CloseDate IS NULL
) vals
where total > 1
Hi helpful clever people, I am running into an issue when trying to prepare a query. I am trying to join a count of sales and a running total of sales to a prebuilt temp table.
The temp table (TMP_WEEK_SHOP) just has 2 rows, a list of week codes(WEEK_ID) and then an entry with that code for every location(SHOP_ID).
Now my issue is that no matter what i do, my queries will always omit results from the temp table if there were no sales for that location during that week. I need all entries for every week to be entered, even the 0 sales shops.
From what i can gather a left outer join should give me this, but no matter what i have tried it keeps omitting any shops without sales. I should say I am running on an SQL Server 2005 environment with Server Management Studio.
Any help at all would be fantastic!
USE CX
SELECT tws.WEEK_ID as Week, isnull(tws.SHOP_ID,0)as Shop, isnull(count(sal.SALES_ITEMS_ID),0)Sales, isnull(sum(sal.LINEVALUE),0)Sales_Value
FROM TMP_WEEK_SHOP tws
JOIN CX_DATES dat
on dat.WEEK_ID=tws.WEEK_ID
LEFT OUTER JOIN CX_SALES_ITEMS sal
on sal.DATE_ID=dat.DATE_ID
and sal.SHOP_NUM=tws.SHOP_ID
JOIN CX_STYLES sty
on sal.STY_QUAL = sty.STY_QUAL
WHERE sty.STY_RET_TYPE='BIKES'
and (sal.SHOP_NUM='70006' or sal.SHOP_NUM='70008' or sal.SHOP_NUM='70010' or sal.SHOP_NUM='70018' or sal.SHOP_NUM='70028' or sal.SHOP_NUM='70029' or sal.SHOP_NUM='70012' or sal.SHOP_NUM='70016' or sal.SHOP_NUM='70026')
GROUP BY tws.WEEK_ID, tws.SHOP_ID
ORDER BY tws.WEEK_ID, tws.SHOP_ID
This is your query, formatted a bit better so I can read it:
SELECT tws.WEEK_ID as Week, isnull(tws.SHOP_ID,0)as Shop,
isnull(count(sal.SALES_ITEMS_ID),0)Sales, isnull(sum(sal.LINEVALUE),0)Sales_Value
FROM TMP_WEEK_SHOP tws JOIN
CX_DATES dat
on dat.WEEK_ID = tws.WEEK_ID LEFT OUTER JOIN
CX_SALES_ITEMS sal
on sal.DATE_ID = dat.DATE_ID and
sal.SHOP_NUM = tws.SHOP_ID JOIN
CX_STYLES sty
on sal.STY_QUAL = sty.STY_QUAL
WHERE sty.STY_RET_TYPE='BIKES' and
(sal.SHOP_NUM in ('70006', '70008', '70010', '70018', '70028', '70029', '70012', '70016', '70026')
GROUP BY tws.WEEK_ID, tws.SHOP_ID
ORDER BY tws.WEEK_ID, tws.SHOP_ID;
You have three problems that are "undoing" the left outer join. The inner join condition will fail when sal.STY_QUAL is NULL. SImilarly, the where conditions have the same problem.
You need for all the joins to be left outer joins and to move the where conditions to on clauses:
SELECT tws.WEEK_ID as Week, isnull(tws.SHOP_ID,0)as Shop,
isnull(count(sal.SALES_ITEMS_ID),0)Sales, isnull(sum(sal.LINEVALUE),0)Sales_Value
FROM TMP_WEEK_SHOP tws JOIN
CX_DATES dat
on dat.WEEK_ID = tws.WEEK_ID LEFT OUTER JOIN
CX_SALES_ITEMS sal
on sal.DATE_ID = dat.DATE_ID and
sal.SHOP_NUM = tws.SHOP_ID and
sal.SHOP_NUM in ('70006', '70008', '70010', '70018', '70028', '70029', '70012', '70016', '70026'
) LEFT OUTER JOIN
CX_STYLES sty
on sal.STY_QUAL = sty.STY_QUAL and
sty.STY_RET_TYPE='BIKES'
GROUP BY tws.WEEK_ID, tws.SHOP_ID
ORDER BY tws.WEEK_ID, tws.SHOP_ID;
In addition, count() never returns a NULL value, so using isnull() or coalesce() is unnecessary.
your other join onvolving CX_SALES_ITEMS sal needs to be an left outer join as well
Why, in this query, is the final 'WHERE' clause needed to limit duplicates?
The first LEFT JOIN is linking programs to entities on a UID
The first INNER JOIN is linking programs to a subquery that gets statistics for those programs, by linking on a UID
The subquery (that gets the StatsForDistributorClubs subset) is doing a grouping on UID columns
So, I would've thought that this would all be joining unique records anyway so we shouldn't get row duplicates
So why the need to limit based on the final WHERE by ensuring the 'program' is linked to the 'entity'?
(irrelevant parts of query omitted for clarity)
SELECT LmiEntity.[DisplayName]
,StatsForDistributorClubs.*
FROM [Program]
LEFT JOIN
LMIEntityProgram
ON LMIEntityProgram.ProgramUid = Program.ProgramUid
INNER JOIN
(
SELECT e.LmiEntityUid,
sp.ProgramUid,
SUM(attendeecount) [Total attendance],
FROM LMIEntity e,
Timetable t,
TimetableOccurrence [to],
ScheduledProgramOccurrence spo,
ScheduledProgram sp
WHERE
t.LicenseeUid = e.lmientityUid
AND [to].TimetableOccurrenceUid = spo.TimetableOccurrenceUid
AND sp.ScheduledProgramUid = spo.ScheduledProgramUid
GROUP BY e.lmientityUid, sp.ProgramUid
) AS StatsForDistributorClubs
ON Program.ProgramUid = StatsForDistributorClubs.ProgramUid
INNER JOIN LmiEntity
ON LmiEntity.LmiEntityUid = StatsForDistributorClubs.LmiEntityUid
LEFT OUTER JOIN Region
ON Region.RegionId = LMIEntity.RegionId
WHERE (
[Program].LicenseeUid = LmiEntity.LmiEntityUid
OR
[LMIEntityProgram].LMIEntityUid = LmiEntity.LmiEntityUid
)
If you were grouping in your outer query, the extra criteria probably wouldn't be needed, but only your inner query is grouped. Your LEFT JOIN to a grouped inner query can still result in multiple records being returned, for that matter any of your JOINs could be the culprit.
Without seeing sample of duplication it's hard to know where the duplicates originate from, but GROUPING on the outer query would definitely remove full duplicates, or revised JOIN criteria could take care of it.
You have in result set:
SELECT LmiEntity.[DisplayName]
,StatsForDistributorClubs.*
I suppose that you dublicates comes from LMIEntityProgram.
My conjecture: LMIEntityProgram - is a bridge table with both LmiEntityId an ProgramId, but you join only by ProgramId.
If you have several LmiEntityId for single ProgramId - you must have dublicates.
And this dublicates you're filtering in WHERE:
[LMIEntityProgram].LMIEntityUid = LmiEntity.LmiEntityUid
You can do it in JOIN:
LEFT JOIN LMIEntityProgram
ON LMIEntityProgram.ProgramUid = Program.ProgramUid
AND [LMIEntityProgram].LMIEntityUid = LmiEntity.LmiEntityUid
I'm building a system in which there are the following tables:
Song
Broadcast
Station
Follow
User
A user follows stations, which have songs on them through broadcasts.
I'm building a "feed" of songs for a user based on the stations they follow.
Here's the query:
SELECT DISTINCT ON ("broadcasts"."created_at", "songs"."id") songs.*
FROM "songs"
INNER JOIN "broadcasts" ON "songs"."shared_id" = "broadcasts"."song_id"
INNER JOIN "stations" ON "broadcasts"."station_id" = "stations"."id"
INNER JOIN "follows" ON "stations"."id" = "follows"."station_id"
WHERE "follows"."user_id" = 2
ORDER BY broadcasts.created_at desc
LIMIT 18
Note: shared_id is the same as id.
As you can see I'm getting duplicate results, which I don't want. I found out from a previous question that this was due to selecting distinct on broadcasts.created_at.
My question is: How do I modify this query so it will return only unique songs based on their id but still order by broadcasts.created_at?
Try this solution:
SELECT a.maxcreated, b.*
FROM
(
SELECT bb.song_id, MAX(bb.created_at) AS maxcreated
FROM follows aa
INNER JOIN broadcasts bb ON aa.station_id = bb.station_id
WHERE aa.user_id = 2
GROUP BY bb.song_id
) a
INNER JOIN songs b ON a.song_id = b.id
ORDER BY a.maxcreated DESC
LIMIT 18
The FROM subselect retrieves distinct song_ids that are broadcasted by all stations the user follows; it also gets the latest broadcast date associated with each song. We have to encase this in a subquery because we have to GROUP BY on the columns we're selecting from, and we only want the unique song_id and the maxdate regardless of the station.
We then join that result in the outer query to the songs table to get the song information associated with each unique song_id
You can use Common Table Expressions (CTE) if you want a cleaner query (nested queries make things harder to read)
I would look like this:
WITH a as (
SELECT bb.song_id, MAX(bb.created_at) AS maxcreated
FROM follows aa
INNER JOIN broadcasts bb ON aa.station_id = bb.station_id
INNER JOIN songs cc ON bb.song_id = cc.shared_id
WHERE aa.user_id = 2
GROUP BY bb.song_id
)
SELECT
a.maxcreated,
b.*
FROM a INNER JOIN
songs b ON a.song_id = b.id
ORDER BY
a.maxcreated DESC
LIMIT 18
Using a CTE offers the advantages of improved readability and ease in maintenance of complex queries. The query can be divided into separate, simple, logical building blocks. These simple blocks can then be used to build more complex, interim CTEs until the final result set is generated.
Try by adding GROUP BY Songs.id
I had a very similar query I was doing between listens, tracks and albums and it took me a long while to figure it out (hours).
If you use a GROUP_BY songs.id, you can get it to work by ordering by MAX(broadcasts.created_at) DESC.
Here's what the full SQL looks like:
SELECT songs.* FROM "songs"
INNER JOIN "broadcasts" ON "songs"."shared_id" = "broadcasts"."song_id"
INNER JOIN "stations" ON "broadcasts"."station_id" = "stations"."id"
INNER JOIN "follows" ON "stations"."id" = "follows"."station_id"
WHERE "follows"."user_id" = 2
GROUP BY songs.id
ORDER BY MAX(broadcasts.created_at) desc
LIMIT 18;