Custom Report on User Data - SQL Server - sql-server-2008

Given the following very simple table:
Create Table tblUserLogins (
LoginNumber int PRIMARY KEY IDENTITY(1,1),
Username varchar(100),
LoginTime datetime
)
Basically when a user logs into the site, a record is created in this table, indicating the user logged in. (For security reasons the developers in my team do not have access to the tables holding the login information, so this was a work-around).
What I need, is some help writing a query which will actually return me the username of the user who logged on the most during a given period (supplied as input values to the procedure).
I can select the data between any 2 given dates using the following:
SELECT * FROM tblUserLogins
WHERE LoginTime BETWEEN #DateFrom AND #DateTo
However I am not sure how I can aggregate the user data, without first dumping the contents of the above query to a temporary table.
Any help would be gratefully received.

SELECT TOP 1
Username,
COUNT(Username) as Total
FROM
tblUserLogins
WHERE
LoginTime BETWEEN #DateFrom AND #DateTo
GROUP BY
Username
ORDER BY
Total DESC

Here it is a full example:
Create Table #tblUserLogins (
LoginNumber int PRIMARY KEY IDENTITY(1,1),
Username varchar(100),
LoginTime datetime
)
declare #DateFrom datetime, #DateTo datetime
select #DateFrom = getdate()
insert into #tblUserLogins values ('test1', getdate())
insert into #tblUserLogins values ('test2', getdate())
insert into #tblUserLogins values ('test1', getdate())
select #DateTo = getdate()
SELECT TOP 1 Username, count(LoginTime) 'Total' FROM #tblUserLogins
WHERE LoginTime BETWEEN #DateFrom AND #DateTo
GROUP BY UserName
ORDER BY Total, Username desc
drop table #tblUserLogins
If there are more than one username with the same number of entries, it'll only show one (with the name most near z).

Related

This Delete statement in a MySql stored procedure does not work

I have this stored procedure to store errors in an Elmah table:
CREATE DEFINER=`b4d7eb33be6399`#`%` PROCEDURE `elmah_LogError`(
IN ErrorId CHAR(36),
IN Application varchar(60),
IN Host VARCHAR(30),
IN Type VARCHAR(100),
IN Source VARCHAR(60),
IN Message VARCHAR(500),
IN User VARCHAR(50),
IN AllXml TEXT,
IN StatusCode INT(10),
IN TimeUtc DATETIME
)
MODIFIES SQL DATA
BEGIN
INSERT INTO `elmah_error` (
`ErrorId`,
`Application`,
`Host`,
`Type`,
`Source`,
`Message`,
`User`,
`AllXml`,
`StatusCode`,
`TimeUtc`
) VALUES (
ErrorId,
Application,
Host,
Type,
Source,
Message,
User,
AllXml,
StatusCode,
TimeUtc
);
DELETE FROM `elmah_error` WHERE ErrorId NOT IN (
SELECT ErrorId
FROM (
SELECT ErrorId
FROM `elmah_error`
ORDER BY TimeUtc DESC
LIMIT 100
) x
);
END
I added the DELETE part to keep 100 last records only. The problem is that it does nothing. However, if I take this DELETE code alone and run it in a query window inside MySql Workbench, it correctly deletes records after the 100 I want to keep. Strange no?
Put backticks on column names:
DELETE FROM `elmah_error` WHERE `ErrorId` NOT IN (
SELECT `ErrorId`
FROM (
SELECT `ErrorId`
FROM `elmah_error`
ORDER BY `TimeUtc` DESC
LIMIT 100
) x
);
Otherwhise they are threated as variables (because of same names) within procedure body.
I had a similar issue where I was trying to delete records older than x days (like every example out there). The problem was TimeUtc was also a parameter thus I needed to reference the table: WHERE elmah_error.TimeUtc < (NOW() - INTERVAL 3 MONTH);
Probably the same issue with ErrorId.

MySQL insert adds rows with id's that shouldn't exist

I'm facing something weird here, i have a database table from which i select certain rows, in total ~43.000, these fetched rows contain a field called cns_id (note, this is not the primary key/auto increment field), and are inserted into a diffrent table.
Although everything seems to run fine, each time i end up with 94 inserted rows that have a cns_id of 2147483647, an id which doesn't even exist inside the table from which i fetch the data.
Below is the stored procedure i'm using, although this issue also happens when i perform the same kind of query with Eloquent.
DELIMITER //
CREATE PROCEDURE FillCnsGroupWithRates
(
IN groupId INT(10)
)
BEGIN
SET #var1 = groupId;
INSERT INTO
cns_group_rates
(
cns_id,
other_cns_id,
ppc,
pps,
max_price,
notes,
cns_group_id,
created_at,
updated_at
)
SELECT
cns_id,
other_cns_id,
ppc,
pps,
max_price,
notes,
#var1,
now(),
now()
FROM
cns_rates
WHERE
cns_id NOT LIKE '319%'
AND
client_id IS NULL
AND
subordinates IS NULL
AND
valid_from IS NULL;
END //
DELIMITER ;

Update query - incrementing int field value and if statement

I am currently learning SQL through my local mysql db. I have a table named transactions that has 5 fields. I am running an update query for column where name = jane. Essentially, I want to integrate an if statement when date_created – tran_date = 1 month to reset values to transaction = 0, tran_date = 0000-00-00, and change date_created to the new current date.
Query(help)- UPDATED
UPDATE transactions SET transactions = transactions + 1, tran_date = CURDATE() WHERE name = 'jim'
Create tables and set values:
CREATE TABLE transactions
(
id int auto_increment primary key,
date_created DATE,
name varchar(20),
transactions int(6),
tran_date DATE
);
INSERT INTO transactions
(date_created, name, transactions, tran_date)
VALUES
(NOW(), 'jim', 0, 0000-00-00),
(NOW(), 'jane', 0, 0000-00-00);
Your updatesyntax is wrong:
UPDATE transactions SET transactions = transactions + 1, tran_date = CURDATE() WHRE name = 'jim'
You have not to use AND in set clause. You must use a comma at this place.

SQL - CURDATE() + 4 random digits (2013-10-02-0001)

create table Table1(
DateIdentify CHAR(15),
primary key(DateIdentify)
);
Insert into Table1 (DateIdentify) VALUES('?');
How I want the 'DateIdentify' to look: (20131002-0001) with 0001 being some sort of an auto incrementer that starts at 0001 and goes up every insert and 20131002 coming from CURDATE(), so adding CURDATE() + 4 digits. I'm wondering if this is possible? If so, could anyone please point me in the right direction?
EDIT:
CREATE TABLE Table1(
IdTable1 int auto_increment NOT NULL,
Date1 datetime,
);
You have to separate your datetime and the auto increment field.
Create your table like this using an auto-increment int field, and your datetime.
For example:
CREATE TABLE Table1(
IdTable1 int PRIMARY KEY IDENTITY(1,1) NOT NULL,
Date datetime
)
Then, you don't have to insert anything in IdTable1 because of it is auto-incremented thanks to keyword IDENTITY (SQL do the auto-increment for you)
NOTE : I wrote this for SQLServer, if you use another database, the code can change a little bit.
Which one do you use ?
EDIT :
You can also do some insert like this :
INSERT INTO Table1
(
Date
)
VALUES
(
'2013-10-02'
)
In case a solution is required as, having only one column with values in desired format you can create a function as:
create function dbo.fn_GetDateIdentify ()
returns varchar(15)
as
begin
declare #DateIdentify varchar(15);
select #DateIdentify =
(select convert (varchar(8),GETDATE (),112) +
'-' +
right ('00000' + cast (
(
(
case when Not exists (select ROW_NUMBER() over( order by (select 1)) from Table1 ) then 1
else (select top 1 ROW_NUMBER() over( order by (select 1)) as currentRownumber from Table1 order by currentRownumber desc) + 1
end
)
)
as varchar(4))
,4));
return #DateIdentify;
end
go;
and then use the function in insert statement as :
insert into Table1 (DateIdentify)
select dbo.fn_GetDateIdentify();
Hope this helps!

Should I use cursors in my SQL procedure?

I have a table that contains computer login and logoff events. Each row is a separate event with a timestamp, machine name, login or logoff event code and other details. I need to create a SQL procedure that goes through this table and locates corresponding login and logoff event and insert new rows into another table that contain the machine name, login time, logout time and duration time.
So, should I use a cursor to do this or is there a better way to go about this? The database is pretty huge so efficiency is certainly a concern. Any suggested pseudo code would be great as well.
[edit : pulled from comment]
Source table:
History (
mc_id
, hs_opcode
, hs_time
)
Existing data interpretation:
Login_Event = unique mc_id, hs_opcode = 1, and hs_time is the timestamp
Logout_Event = unique mc_id, hs_opcode = 2, and hs_time is the timestamp
First, your query will be simpler (and faster) if you can order the data in such a way that you don't need a complex subquery to pair up the rows. Since MySQL doesn't support CTE to do this on-the-fly, you'll need to create a temporary table:
CREATE TABLE history_ordered (
seq INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
hs_id INT,
mc_id VARCHAR(255),
mc_loggedinuser VARCHAR(255),
hs_time DATETIME,
hs_opcode INT
);
Then, pull and sort from your original table into the new table:
INSERT INTO history_ordered (
hs_id, mc_id, mc_loggedinuser,
hs_time, hs_opcode)
SELECT
hs_id, mc_id, mc_loggedinuser,
hs_time, hs_opcode
FROM history ORDER BY mc_id, hs_time;
You can now use this query to correlate the data:
SELECT li.mc_id,
li.mc_loggedinuser,
li.hs_time as login_time,
lo.hs_time as logout_time
FROM history_ordered AS li
JOIN history_ordered AS lo
ON lo.seq = li.seq + 1
AND li.hs_opcode = 1;
For future inserts, you can use a trigger like below to keep your duration table updated automatically:
DELIMITER $$
CREATE TRIGGER `match_login` AFTER INSERT ON `history`
FOR EACH ROW
BEGIN
IF NEW.hs_opcode = 2 THEN
DECLARE _user VARCHAR(255);
DECLARE _login DATETIME;
SELECT mc_loggedinuser, hs_time FROM history
WHERE hs_time = (
SELECT MAX(hs_time) FROM history
WHERE hs_opcode = 1
AND mc_id = NEW.mc_id
) INTO _user, _login;
INSERT INTO login_duration
SET machine = NEW.mc_id,
logout = NEW.hs_time,
user = _user,
login = _login;
END IF;
END$$
DELIMITER ;
CREATE TABLE dummy (fields you'll select data into, + additional fields as needed)
INSERT INTO dummy (columns from your source)
SELECT * FROM <all the tables where you need data for your target data set>
UPDATE dummy SET col1 = CASE WHEN this = this THEN that, etc
INSERT INTO targetTable
SELECT all columns FROM dummy
Without any code that you're working on.. it'll be hard to see if this approach will be any useful.. There may be some instances when you really need to loop through things.. and some instances when this approach can be used instead..
[EDIT: based on poster's comment]
Can you try executing this and see if you get the desired results?
INSERT INTO <your_target_table_here_with_the_three_columns_required>
SELECT li.mc_id, li.hs_time AS login_time, lo.hs_time AS logout_time
FROM
history AS li
INNER JOIN history AS lo
ON li.mc_id = lo.mc_id
AND li.hs_opcode = 1
AND lo.hs_opcode = 2
AND lo.hs_time = (
SELECT min(hs_time) AS hs_time
FROM history
WHERE hs_time > li.hs_time
AND mc_id = li.mc_id
)