Get last log line per unique host from table - sql

I have inherited an application that logs the results of certain daily commands that run on multiple hosts to an MS-SQL table. Now I've been asked to provide a view/query that displays the last log line per host to get an overview of the last results.
The table is similar to this:
------------------------------
|HOST |LAST_RUN |RESULT |
------------------------------
|SERVER1 |13-07-2009 |OK |
|SERVER2 |13-07-2009 |Failed |
|SERVER1 |12-07-2009 |OK |
|SERVER2 |12-07-2009 |OK |
|SERVER3 |11-07-2009 |OK |
------------------------------
In this case the query should output:
------------------------------
|HOST |LAST_RUN |RESULT |
------------------------------
|SERVER1 |13-07-2009 |OK |
|SERVER2 |12-07-2009 |Failed |
|SERVER3 |11-07-2009 |OK |
------------------------------
...as these are the last lines for each of the hosts.
I realise that it might be something simple I'm missing, but I just can't seem to get it right :-(
Thanks,
Mark.

Here's a quick version:
SELECT lt.Host, lt.Last_Run, lt.Results
from LogTable lt
inner join (select Host, max(Last_Run) Last_Run
from LogTable
group by Host) MostRecent
on MostRecent.Host = lt.Host
and MostRecent.Last_run = lt.Last_Run
This should work in most any SQL system. The ranking functions in SQL Server 2005 or 2008 might work a bit better.

Select Host, Last_Run, Result from
(
select ROW_NUMBER() OVER (PARTITION BY Host ORDER BY Last_Run DESC) AS row_number,
Host, Last_Run, Result from Table1
) tempTable
where row_number = 1

select host, max (last_run) from t group by host
Gets you the host and last_run you want. Then
(select host as h, last_run as lr, result as r from t) inner join (select host as h_max, max (last_run) as lr_max from t group by host) on h=h_max, lr=lr_max
Forgive my sql if not exact -- not trying it or looking it up, but you get the idea.

I think there's a typo at SERVER2's LAST_RUN date.
This provides the same result as all the other great answers without the use of a subquery:
select t."HOST"
, t."LAST_RUN"
, t."RESULT"
from yourtable t
left outer join yourtable t2
on t."HOST" = t2."HOST"
and
t2."LAST_RUN" > t."LAST_RUN"
where t2."RESULT" is null;
And the resultset looks like this:
|HOST |LAST_RUN |RESULT |
------------------------------
|SERVER1 |13-07-2009 |OK |
|SERVER2 |13-07-2009 |Failed |
|SERVER3 |11-07-2009 |OK |

Related

Why does adding a SUM(column) throw a group by error [SQL]

I found some similar questions, but none of the solutions would work, nor did they explain what was causing the issue.
I have a working query
SELECT pages.pageString pageName, timeSpent
FROM
(SELECT `page_id`, SUM(`time_spent`) as timeSpent
FROM `pageViews`
WHERE `time_spent` > 0
GROUP BY `page_id`) myTable
JOIN pages ON pages.id = page_id
ORDER BY timeSpent DESC
LIMIT 5
This returns results that look like
+------------------------------+-----------+
| pageName | timeSpent |
+------------------------------+-----------+
| page 1 | 394292 |
| page 2 | 66990 |
| page 3 | 53896 |
| page 4 | 37796 |
| page 5 | 14982 |
+------------------------------+-----------+
I'd like to add a column containing the percentage of timeSpent relative to the other pages, to start I added a SUM(timeSpent) to my query but that throws an error
In aggregated query without GROUP BY, expression #1 of SELECT list contains nonaggregated column 'pages.pageString'
Im not sure why this column is effected by adding this new column to the select statement.
Sadly any solution involving changing sql settings won't work due to company policy.
I appreciate any advice
UPDATE
The failing sql statement is
SELECT pages.pageString pageName, timeSpent FROM
(SELECT `page_id`, SUM(`time_spent`) as timeSpent FROM
`pageViews` WHERE `time_spent` > 0 GROUP BY `page_id`) myTable
JOIN pages ON pages.id = page_id ORDER BY timeSpent DESC LIMIT 5
As per the first answer I added a groupBy which solves the error
SELECT pages.pageString pageName, timeSpent, SUM(timeSpent) FROM
(SELECT `page_id`, SUM(`time_spent`) as timeSpent FROM `pageViews` WHERE `time_spent` > 0 GROUP BY `page_id`) myTable
JOIN pages ON pages.id = page_id GROUP BY pageName ORDER BY timeSpent DESC LIMIT 5
This however does not give the proper output
+------------------------------+-----------+----------------+
| pageName | timeSpent | SUM(timeSpent) |
+------------------------------+-----------+----------------+
| page 1. | 390210 | 390210 |
| page 2 | 66972 | 66972 |
| page3 | 52332 | 52332 |
| page4 | 25454 | 25454 |
| page5 | 13552 | 13552 |
+------------------------------+-----------+----------------+
Ideally this SUM(timeSpent) would be 390210+ 66972 + 52332 + 25454 + 13552 so that I may do timeSpent / SUM(timeSpent)
You did not say where you tried to put the sum(timeSpent) but I believe one can try to reconstruct with the error message:
In aggregated query without GROUP BY, expression #1 of SELECT list contains nonaggregated column 'pages.pageString'
It says what the problem is. You added sum(timeSpent) to the projection, but the SQL statement does not have a GROUP BY, in particular it mentions the first item which should be aggregated pages.pageString.
It would mention the other ones too, once you fix this one.
On the other hand, please make sure you post exactly the failing SQL statement instead of trying to describe how to get the error you have. It's better for us who try to help.
Update:
You have two tables/views pages and pageViews. The first one is used to get the page name. I would just focus on the time calculation to make things easier. Figuring out the name afterwards is simple, because it is directly connected to the page_id.
The first information you want is the sum of all times spent so that you can calculate the ratio to this sum.
This is simply an aggregation where you sum the times over all pages.
The second information you want is the sum of the times per page_id. You already know how to do that. You group by the page_id while aggregating the sums of each.
Try to put those two together now. You have the first statement of which the result shall be applied to each row of the second statement so that you get the table form page_id, time_spent_page, time_spent_all.
When you have step 3 then it is easy to add the page_name now, since you have the page_id which is required for a simple join.
I tried no to give away the solution. Maybe you like to try again following the steps above. If you have difficulties, simply leave a comment (maybe showing how far you got).
It might look complex in the beginning, but once you have done that successfully I hope you'll see that it can be simple.
Adding a column containing the percentage of timeSpent relative to the sum of all pages
SELECT pages.pageString pageName, timeSpent,
, timeSpent / sum(timeSpent) over() * 100 p
FROM
(SELECT `page_id`, SUM(`time_spent`) as timeSpent
FROM `pageViews`
WHERE `time_spent` > 0
GROUP BY `page_id`) myTable
JOIN pages ON pages.id = page_id
ORDER BY timeSpent DESC
LIMIT 5

SQL: SUM of MAX values WHERE date1 <= date2 returns "wrong" results

Hi stackoverflow users
I'm having a bit of a problem trying to combine SUM, MAX and WHERE in one query and after an intense Google search (my search engine skills usually don't fail me) you are my last hope to understand and fix the following issue.
My goal is to count people in a certain period of time and because a person can visit more than once in said period, I'm using MAX. Due to the fact that I'm defining people as male (m) or female (f) using a string (for statistic purposes), CHAR_LENGTH returns the numbers I'm in need of.
SELECT SUM(max_pers) AS "People"
FROM (
SELECT "guests"."id", MAX(CHAR_LENGTH("guests"."gender")) AS "max_pers"
FROM "guests"
GROUP BY "guests"."id")
So far, so good. But now, as stated before, I'd like to only count the guests which visited in a certain time interval (for statistic purposes as well).
SELECT "statistic"."id", SUM(max_pers) AS "People"
FROM (
SELECT "guests"."id", MAX(CHAR_LENGTH("guests"."gender")) AS "max_pers"
FROM "guests"
GROUP BY "guests"."id"),
"statistic", "guests"
WHERE ( "guests"."arrival" <= "statistic"."from" AND "guests"."departure" >= "statistic"."to")
GROUP BY "statistic"."id"
This query returns the following, x = desired result:
x * (x+1)
So if the result should be 3, it's 12. If it should be 5, it's 30 etc.
I probably could solve this algebraic but I'd rather understand what I'm doing wrong and learn from it.
Thanks in advance and I'm certainly going to answer all further questions.
PS: I'm using LibreOffice Base.
EDIT: An example
guests table:
ID | arrival | departure | gender |
10 | 1.1.14 | 10.1.14 | mf |
10 | 15.1.14 | 17.1.14 | m |
11 | 5.1.14 | 6.1.14 | m |
12 | 10.2.14 | 24.2.14 | f |
13 | 27.2.14 | 28.2.14 | mmmmmf |
statistic table:
ID | from | to | name |
1 | 1.1.14 | 31.1.14 |January | expected result: 3
2 | 1.2.14 | 28.2.14 |February| expected result: 7
MAX(...) is the wrong function: You want COUNT(DISTINCT ...).
Add proper join syntax, simplify (and remove unnecessary quotes) and this should work:
SELECT s.id, COUNT(DISTINCT g.id) AS People
FROM statistic s
LEFT JOIN guests g ON g.arrival <= s."from" AND g.departure >= s."too"
GROUP BY s.id
Note: Using LEFT join means you'll get a result of zero for statistics ids that have no guests. If you would rather no row at all, remove the LEFT keyword.
You have a very strange data structure. In any case, I think you want:
SELECT s.id, sum(numpersons) AS People
FROM (select g.id, max(char_length(g.gender)) as numpersons
from guests g join
statistic s
on g.arrival <= s."from" AND g.departure >= s."too"
group by g.id
) g join
GROUP BY s.id;
Thanks for all your inputs. I wasn't familiar with JOIN but it was necessary to solve my problem.
Since my databank is designed in german, I made quite the big mistake while translating it and I'm sorry if this caused confusion.
Selecting guests.id and later on grouping by guests.id wouldn't make any sense since the id is unique. What I actually wanted to do is select and group the guests.adr_id which links a visiting guest to an adress databank.
The correct solution to my problem is the following code:
SELECT statname, SUM (numpers) FROM (
SELECT statistic.name AS statname, guests.adr_id, MAX( CHAR_LENGTH( guests.gender ) ) AS numpers
FROM guests
JOIN statistics ON (guests.arrival <= statistics.too AND guests.departure >= statistics.from )
GROUP BY guests.adr_id, statistic.name )
GROUP BY statname
I also noted that my database structure is a mess but I created it learning by doing and haven't found any time to rewrite it yet. Next time posting, I'll try better.

Query to get list active queries in my SQL Server

I want to get a list of information of active queries that are running in my SQL Server (in order to kill a few of those queries).
I want a query to get this required information:
| query_id (if possible) | query_text | query_start_time | time_elapsed | host_name | user_group | query_status |
I am new to SQL Server please suggest....
Try this
select
r.session_id,
r.status,
r.command,
r.cpu_time,
r.total_elapsed_time,
t.text
from sys.dm_exec_requests as r
cross apply sys.dm_exec_sql_text(r.sql_handle) as t

MYSQL - Combining Two Results in One Query

I have a query I need to perform to show search results for a project. What needs to happen, I need to sort the results by the "horsesActiveDate" and this applies to all of them except for any ad with the adtypesID=7. Those results are sorted by date but they must always result after all other ads.
So I will have all my ads in the result set be ordered by the Active Date AND adtypesID != 7. After that, I need all adtypesID=7 to be sorted by Active Date and appended at the bottom of all the results.
I'm hoping to put this in one query instead of two and appending them together in PHP. The way the code is written, I have to find a way to get it all in one query.
So here is my original query which has worked great until I had to ad the adtypesID=7 which has different sorting requirements.
This is the query that exists now that doesn't take into account the adtypesID for sorting.
SELECT
horses.horsesID,
horsesDescription,
horsesActiveDate,
adtypesID,
states.statesName,
horses_images.himagesPath
FROM horses
LEFT JOIN states ON horses.statesID = states.statesID
LEFT JOIN horses_images ON horses_images.himagesDefault = 1 AND horses_images.horsesID = horses.horsesID AND horses_images.himagesPath != ''
WHERE
horses.horsesStud = 0
AND horses.horsesSold = 0
AND horses.horsesID IN
(
SELECT DISTINCT horses.horsesID
FROM horses
LEFT JOIN horses_featured ON horses_featured.horsesID = horses.horsesID
WHERE horses.horsesActive = 1
)
ORDER BY adtypesID, horses.horsesActiveDate DESC
My first thought was to do two queries where one looked for all the ads that did not contain adtypesID=7 and sort those as the query does, then run a second query to find only those ads with adtypesID=7 and sort those by date. Then take those two results and append them to each other. Since I need to get this all into one query, I can't use a php function to do that.
Is there a way to merge the two query results one after the other in mysql? Is there a better way to run this query that will accomplish this sorting?
The Ideal Results would be as below (I modified the column names so they would be shorter):
ID | Description | ActiveDate | adtypesID | statesName | himagesPath
___________________________________________________________________________
3 | Ad Text | 06-01-2010 | 3 | OK | image.jpg
2 | Ad Text | 05-31-2010 | 2 | LA | image1.jpg
9 | Ad Text | 03-01-2010 | 4 | OK | image3.jpg
6 | Ad Text | 06-01-2010 | 7 | OK | image5.jpg
6 | Ad Text | 05-01-2010 | 7 | OK | image5.jpg
6 | Ad Text | 04-01-2010 | 7 | OK | image5.jpg
Any help that can be provided will be greatly appreciated!
I am not sure about the exact syntax in MySQL, but something like
ORDER BY case when adtypesID = 7 then 2 else 1 end ASC, horses.horsesActiveDate DESC
would work in many other SQL dielects.
Note that most SQL dialects allow the order by to not only be a column, but an expression.
This should work:
ORDER BY (adtypesID = 7) ASC, horses.horsesActiveDate DESC
Use a Union to append two queries together, like this:
SELECT whatever FROM wherever ORDER BY something AND adtypesID!=7
UNION
SELECT another FROM somewhere ORDER BY whocares AND adtypesID=7
http://dev.mysql.com/doc/refman/5.0/en/union.html
I re-wrote your query as:
SELECT h.horsesID,
h.horsesDescription,
h.horsesActiveDate,
adtypesID,
s.statesName,
hi.himagesPath
FROM HORSES h
LEFT JOIN STATES s ON s.stateid = h.statesID
LEFT JOIN HORSES_IMAGES hi ON hi.horsesID = h.horsesID
AND hi.himagesDefault = 1
AND hi.himagesPath != ''
LEFT JOIN HORSES_FEATURED hf ON hf.horsesID = h.horsesID
WHERE h.horsesStud = 0
AND h.horsesSold = 0
AND h.horsesActive = 1
ORDER BY (adtypesID = 7) ASC, h.horsesActiveDate DESC
The IN subquery, using a LEFT JOIN and such, will mean that any horse record whose horsesActive value is 1 will be returned - regardless if they have an associated HORSES_FEATURED record. I leave it to you for checking your data to decide if it should really be an INNER JOIN. Likewise for the STATES table relationship...

Need a Complex SQL Query

I need to make a rather complex query, and I need help bad. Below is an example I made.
Basically, I need a query that will return one row for each case_id where the type is support, status start, and date meaning the very first one created (so that in the example below, only the 2/1/2009 John's case gets returned, not the 3/1/2009). The search needs to be dynamic to the point of being able to return all similar rows with different case_id's etc from a table with thousands of rows.
There's more after that but I don't know all the details yet, and I think I can figure it out if you guys (an gals) can help me out here. :)
ID | Case_ID | Name | Date | Status | Type
48 | 450 | John | 6/1/2009 | Fixed | Support
47 | 450 | John | 4/1/2009 | Moved | Support
46 | 451 | Sarah | 3/1/2009 | |
45 | 432 | John | 3/1/2009 | Fixed | Critical
44 | 450 | John | 3/1/2009 | Start | Support
42 | 450 | John | 2/1/2009 | Start | Support
41 | 440 | Ben | 2/1/2009 | |
40 | 432 | John | 1/1/2009 | Start | Critical
...
Thanks a bunch!
Edit:
To answer some people's questions, I'm using SQL Server 2005. And the date is just plain date, not string.
Ok so now I got further in the problem. I ended up with Bliek's solution which worked like a charm. But now I ran into the problem that sometimes the status never starts, as it's solved immediately. I need to include this in as well. But only for a certain time period.
I imagine I'm going to have to check for the case table referenced by FK Case_ID here. So I'd need a way to check for each Case_ID created in the CaseTable within the past month, and then run a search for these in the same table and same manner as posted above, returning only the first result as before. How can I use the other table like that?
As usual I'll try to find the answer myself while waiting, thanks again!
Edit 2:
Seems this is the answer. I don't have access to the full DB yet so I can't fully test it, but it seems to be working with the dummy tables I created, to continue from Bliek's code's WHERE clause:
WHERE RowNumber = 1 AND Case_ID IN (SELECT Case_ID FROM CaseTable
WHERE (Date BETWEEN '2007/11/1' AND '2007/11/30'))
The date's screwed again but you get the idea I'm sure. Thanks for the help everyone! I'll get back if there're more problems, but I think with this info I can improvise my way through most of the SQL problems I currently have to deal with. :)
Maybe something like:
select Case_ID, Name, MIN(date), Status, Type
from table
where type = 'Support'
and status = 'Start'
group by Case_ID, Name, Status, Type
EDIT: You haven't provided a lot of details about what you really want, so I'd suggest that you read all the answers and choose one that suits your problem best. So far I'd say that Tomalak's answer is closest to what you're looking for...
SELECT
c.ID,
c.Case_ID,
c.Name,
c.Date,
c.Status,
c.Type
FROM
CaseTable c
WHERE
c.Type = 'Support'
AND c.Status = 'Start'
AND c.Date = (
SELECT MIN(Date)
FROM CaseTable
WHERE Case_ID = c.Case_ID AND Type = c.Type AND Status = c.Status)
/* GROUP BY only needed when for a given Case_ID several rows
exist that fulfill the WHERE clause */
GROUP BY
c.ID,
c.Case_ID,
c.Name,
c.Date,
c.Status,
c.Type
This query benefits greatly from indexes on the Case_ID, Date, Status and Type columns.
Added value though the fact that the filter on Support and Status only needs to be set in one place.
As an alternative to the GROUP BY clause, you can do SELECT DISTINCT, which would increase readability (this may or may not affect overall performance, I suggest you measure both variants against each other). If you are sure that for no Case_ID in your table two rows exist that have the same Date, you won't need GROUP BY or SELECT DISTINCT at all.
In SQL Server 2005 and beyond I would use Common Table Expressions (CTE). This offers lots of possibilities like so:
With ResultTable (RowNumber
,ID
,Case_ID
,Name
,Date
,Status
,Type)
AS
(
SELECT Row_Number() OVER (PARTITION BY Case_ID
ORDER BY Date ASC)
,ID
,Case_ID
,Name
,Date
,Status
,Type
FROM CaseTable
WHERE Type = 'Support'
AND Status = 'Start'
)
SELECT ID
,Case_ID
,Name
,Date
,Status
,Type
FROM ResultTable
WHERE RowNumber = 1
Don't apologize for your date formatting, it makes more sense that way.
SELECT ID, Case_ID, Name, MIN(Date), Status, Type
FROM caseTable
WHERE Type = 'Support'
AND status = 'Start'
GROUP BY ID, Case_ID, Name, Status, Type