Getting the user name who executed the stored procedure in Database? - sql

Someone ran a stored procedure from his machine, and I just want to know who executed that stored procedure.
I am trying to see sys.dm_exec_procedure_stats but I'm having no luck:
SELECT object_id
FROM sys.dm_exec_procedure_stats
WHERE OBJECT_NAME(object_id,database_id) = 'SpName'

I have used https://dba.stackexchange.com/questions/135078/how-to-get-history-of-queries-executed-with-username-in-sql and updated with my logic and its worked for me
USE master
go
SELECT top 10 sdest.DatabaseName
,sdes.session_id
,sdes.[host_name]
,sdes.[program_name]
,sdes.client_interface_name
,sdes.login_name
,sdes.login_time
,sdes.nt_domain
,sdes.nt_user_name
,sdec.client_net_address
,sdec.local_net_address
,sdest.ObjName
,sdest.Query
FROM sys.dm_exec_sessions AS sdes
INNER JOIN sys.dm_exec_connections AS sdec ON sdec.session_id = sdes.session_id
CROSS APPLY (
SELECT db_name(dbid) AS DatabaseName
,object_id(objectid) AS ObjName
,ISNULL((
SELECT TEXT AS [processing-instruction(definition)]
FROM sys.dm_exec_sql_text(sdec.most_recent_sql_handle)
FOR XML PATH('')
,TYPE
), '') AS Query
FROM sys.dm_exec_sql_text(sdec.most_recent_sql_handle) WHERE OBJECT_NAME(objectid,dbid)='MySp'
) sdest
where sdes.session_id <> ##SPID
--and sdes.nt_user_name = '' -- Put the username here !
ORDER BY sdec.session_id

Related

Create a view in SQL with a row_count column [duplicate]

How to list row count of each table in the database. Some equivalent of
select count(*) from table1
select count(*) from table2
...
select count(*) from tableN
I will post a solution but other approaches are welcome
If you're using SQL Server 2005 and up, you can also use this:
SELECT
t.NAME AS TableName,
i.name as indexName,
p.[Rows],
sum(a.total_pages) as TotalPages,
sum(a.used_pages) as UsedPages,
sum(a.data_pages) as DataPages,
(sum(a.total_pages) * 8) / 1024 as TotalSpaceMB,
(sum(a.used_pages) * 8) / 1024 as UsedSpaceMB,
(sum(a.data_pages) * 8) / 1024 as DataSpaceMB
FROM
sys.tables t
INNER JOIN
sys.indexes i ON t.OBJECT_ID = i.object_id
INNER JOIN
sys.partitions p ON i.object_id = p.OBJECT_ID AND i.index_id = p.index_id
INNER JOIN
sys.allocation_units a ON p.partition_id = a.container_id
WHERE
t.NAME NOT LIKE 'dt%' AND
i.OBJECT_ID > 255 AND
i.index_id <= 1
GROUP BY
t.NAME, i.object_id, i.index_id, i.name, p.[Rows]
ORDER BY
object_name(i.object_id)
In my opinion, it's easier to handle than the sp_msforeachtable output.
A snippet I found at http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=21021 that helped me:
select t.name TableName, i.rows Records
from sysobjects t, sysindexes i
where t.xtype = 'U' and i.id = t.id and i.indid in (0,1)
order by TableName;
To get that information in SQL Management Studio, right click on the database, then select Reports --> Standard Reports --> Disk Usage by Table.
SELECT
T.NAME AS 'TABLE NAME',
P.[ROWS] AS 'NO OF ROWS'
FROM SYS.TABLES T
INNER JOIN SYS.PARTITIONS P ON T.OBJECT_ID=P.OBJECT_ID;
As seen here, this will return correct counts, where methods using the meta data tables will only return estimates.
CREATE PROCEDURE ListTableRowCounts
AS
BEGIN
SET NOCOUNT ON
CREATE TABLE #TableCounts
(
TableName VARCHAR(500),
CountOf INT
)
INSERT #TableCounts
EXEC sp_msForEachTable
'SELECT PARSENAME(''?'', 1),
COUNT(*) FROM ? WITH (NOLOCK)'
SELECT TableName , CountOf
FROM #TableCounts
ORDER BY TableName
DROP TABLE #TableCounts
END
GO
sp_MSForEachTable 'DECLARE #t AS VARCHAR(MAX);
SELECT #t = CAST(COUNT(1) as VARCHAR(MAX))
+ CHAR(9) + CHAR(9) + ''?'' FROM ? ; PRINT #t'
Output:
Well luckily SQL Server management studio gives you a hint on how to do this.
Do this,
start a SQL Server trace and open the activity you are doing (filter
by your login ID if you're not alone and set the application Name
to Microsoft SQL Server Management Studio), pause the trace and discard any results you have recorded till now;
Then, right click a table and select property from the pop up menu;
start the trace again;
Now in SQL Server Management studio select the storage property item on the left;
Pause the trace and have a look at what TSQL is generated by microsoft.
In the probably last query you will see a statement starting with exec sp_executesql N'SELECT
when you copy the executed code to visual studio you will notice that this code generates all the data the engineers at microsoft used to populate the property window.
when you make moderate modifications to that query you will get to something like this:
SELECT
SCHEMA_NAME(tbl.schema_id)+'.'+tbl.name as [table], --> something I added
p.partition_number AS [PartitionNumber],
prv.value AS [RightBoundaryValue],
fg.name AS [FileGroupName],
CAST(pf.boundary_value_on_right AS int) AS [RangeType],
CAST(p.rows AS float) AS [RowCount],
p.data_compression AS [DataCompression]
FROM sys.tables AS tbl
INNER JOIN sys.indexes AS idx ON idx.object_id = tbl.object_id and idx.index_id < 2
INNER JOIN sys.partitions AS p ON p.object_id=CAST(tbl.object_id AS int) AND p.index_id=idx.index_id
LEFT OUTER JOIN sys.destination_data_spaces AS dds ON dds.partition_scheme_id = idx.data_space_id and dds.destination_id = p.partition_number
LEFT OUTER JOIN sys.partition_schemes AS ps ON ps.data_space_id = idx.data_space_id
LEFT OUTER JOIN sys.partition_range_values AS prv ON prv.boundary_id = p.partition_number and prv.function_id = ps.function_id
LEFT OUTER JOIN sys.filegroups AS fg ON fg.data_space_id = dds.data_space_id or fg.data_space_id = idx.data_space_id
LEFT OUTER JOIN sys.partition_functions AS pf ON pf.function_id = prv.function_id
Now the query is not perfect and you could update it to meet other questions you might have, the point is, you can use the knowledge of microsoft to get to most of the questions you have by executing the data you're interested in and trace the TSQL generated using profiler.
I kind of like to think that MS engineers know how SQL server work and, it will generate TSQL that works on all items you can work with using the version on SSMS you are using so it's quite good on a large variety releases prerviouse, current and future.
And remember, don't just copy, try to understand it as well else you might end up with the wrong solution.
Walter
This approaches uses string concatenation to produce a statement with all tables and their counts dynamically, like the example(s) given in the original question:
SELECT COUNT(*) AS Count,'[dbo].[tbl1]' AS TableName FROM [dbo].[tbl1]
UNION ALL SELECT COUNT(*) AS Count,'[dbo].[tbl2]' AS TableName FROM [dbo].[tbl2]
UNION ALL SELECT...
Finally this is executed with EXEC:
DECLARE #cmd VARCHAR(MAX)=STUFF(
(
SELECT 'UNION ALL SELECT COUNT(*) AS Count,'''
+ QUOTENAME(t.TABLE_SCHEMA) + '.' + QUOTENAME(t.TABLE_NAME)
+ ''' AS TableName FROM ' + QUOTENAME(t.TABLE_SCHEMA) + '.' + QUOTENAME(t.TABLE_NAME)
FROM INFORMATION_SCHEMA.TABLES AS t
WHERE TABLE_TYPE='BASE TABLE'
FOR XML PATH('')
),1,10,'');
EXEC(#cmd);
The first thing that came to mind was to use sp_msForEachTable
exec sp_msforeachtable 'select count(*) from ?'
that does not list the table names though, so it can be extended to
exec sp_msforeachtable 'select parsename(''?'', 1), count(*) from ?'
The problem here is that if the database has more than 100 tables you will get the following error message:
The query has exceeded the maximum
number of result sets that can be
displayed in the results grid. Only
the first 100 result sets are
displayed in the grid.
So I ended up using table variable to store the results
declare #stats table (n sysname, c int)
insert into #stats
exec sp_msforeachtable 'select parsename(''?'', 1), count(*) from ?'
select
*
from #stats
order by c desc
Fastest way to find row count of all tables in SQL Refreence (http://www.codeproject.com/Tips/811017/Fastest-way-to-find-row-count-of-all-tables-in-SQL)
SELECT T.name AS [TABLE NAME], I.rows AS [ROWCOUNT]
FROM sys.tables AS T
INNER JOIN sys.sysindexes AS I ON T.object_id = I.id
AND I.indid < 2
ORDER BY I.rows DESC
I want to share what's working for me
SELECT
QUOTENAME(SCHEMA_NAME(sOBJ.schema_id)) + '.' + QUOTENAME(sOBJ.name) AS [TableName]
, SUM(sdmvPTNS.row_count) AS [RowCount]
FROM
sys.objects AS sOBJ
INNER JOIN sys.dm_db_partition_stats AS sdmvPTNS
ON sOBJ.object_id = sdmvPTNS.object_id
WHERE
sOBJ.type = 'U'
AND sOBJ.is_ms_shipped = 0x0
AND sdmvPTNS.index_id < 2
GROUP BY
sOBJ.schema_id
, sOBJ.name
ORDER BY [TableName]
GO
The database is hosted in Azure and the final result is:
Credit: https://www.mssqltips.com/sqlservertip/2537/sql-server-row-count-for-all-tables-in-a-database/
Here is my take on this question. It contains all schemas and lists only tables with rows. YMMV
select distinct schema_name(t.schema_id) as schema_name, t.name as
table_name, p.[Rows]
from sys.tables as t
INNER JOIN sys.indexes as i ON t.OBJECT_ID = i.object_id
INNER JOIN sys.partitions p ON i.object_id = p.OBJECT_ID AND i.index_id =
p.index_id
where p.[Rows] > 0
order by schema_name;
If you use MySQL >4.x you can use this:
select TABLE_NAME, TABLE_ROWS from information_schema.TABLES where TABLE_SCHEMA="test";
Keep in mind that for some storage engines, TABLE_ROWS is an approximation.
The accepted answer didn't work for me on Azure SQL, here's one that did, it's super fast and did exactly what I wanted:
select t.name, s.row_count
from sys.tables t
join sys.dm_db_partition_stats s
ON t.object_id = s.object_id
and t.type_desc = 'USER_TABLE'
and t.name not like '%dss%'
and s.index_id = 1
order by s.row_count desc
You could try this:
SELECT OBJECT_SCHEMA_NAME(ps.object_Id) AS [schemaname],
OBJECT_NAME(ps.object_id) AS [tablename],
row_count AS [rows]
FROM sys.dm_db_partition_stats ps
WHERE OBJECT_SCHEMA_NAME(ps.object_Id) <> 'sys' AND ps.index_id < 2
ORDER BY
OBJECT_SCHEMA_NAME(ps.object_Id),
OBJECT_NAME(ps.object_id)
This sql script gives the schema, table name and row count of each table in a database selected:
SELECT SCHEMA_NAME(schema_id) AS [SchemaName],
[Tables].name AS [TableName],
SUM([Partitions].[rows]) AS [TotalRowCount]
FROM sys.tables AS [Tables]
JOIN sys.partitions AS [Partitions]
ON [Tables].[object_id] = [Partitions].[object_id]
AND [Partitions].index_id IN ( 0, 1 )
-- WHERE [Tables].name = N'name of the table'
GROUP BY SCHEMA_NAME(schema_id), [Tables].name
order by [TotalRowCount] desc
Ref: https://blog.sqlauthority.com/2017/05/24/sql-server-find-row-count-every-table-database-efficiently/
Another way of doing this:
SELECT o.NAME TABLENAME,
i.rowcnt
FROM sysindexes AS i
INNER JOIN sysobjects AS o ON i.id = o.id
WHERE i.indid < 2 AND OBJECTPROPERTY(o.id, 'IsMSShipped') = 0
ORDER BY i.rowcnt desc
I think that the shortest, fastest and simplest way would be:
SELECT
object_name(object_id) AS [Table],
SUM(row_count) AS [Count]
FROM
sys.dm_db_partition_stats
WHERE
--object_schema_name(object_id) = 'dbo' AND
index_id < 2
GROUP BY
object_id
USE DatabaseName
CREATE TABLE #counts
(
table_name varchar(255),
row_count int
)
EXEC sp_MSForEachTable #command1='INSERT #counts (table_name, row_count) SELECT ''?'', COUNT(*) FROM ?'
SELECT table_name, row_count FROM #counts ORDER BY table_name, row_count DESC
DROP TABLE #counts
From this question:
https://dba.stackexchange.com/questions/114958/list-all-tables-from-all-user-databases/230411#230411
I added record count to the answer provided by #Aaron Bertrand that lists all databases and all tables.
DECLARE #src NVARCHAR(MAX), #sql NVARCHAR(MAX);
SELECT #sql = N'', #src = N' UNION ALL
SELECT ''$d'' as ''database'',
s.name COLLATE SQL_Latin1_General_CP1_CI_AI as ''schema'',
t.name COLLATE SQL_Latin1_General_CP1_CI_AI as ''table'' ,
ind.rows as record_count
FROM [$d].sys.schemas AS s
INNER JOIN [$d].sys.tables AS t ON s.[schema_id] = t.[schema_id]
INNER JOIN [$d].sys.sysindexes AS ind ON t.[object_id] = ind.[id]
where ind.indid < 2';
SELECT #sql = #sql + REPLACE(#src, '$d', name)
FROM sys.databases
WHERE database_id > 4
AND [state] = 0
AND HAS_DBACCESS(name) = 1;
SET #sql = STUFF(#sql, 1, 10, CHAR(13) + CHAR(10));
PRINT #sql;
--EXEC sys.sp_executesql #sql;
You can copy, past and execute this piece of code to get all table record counts into a table. Note: Code is commented with instructions
create procedure RowCountsPro
as
begin
--drop the table if exist on each exicution
IF OBJECT_ID (N'dbo.RowCounts', N'U') IS NOT NULL
DROP TABLE dbo.RowCounts;
-- creating new table
CREATE TABLE RowCounts
( [TableName] VARCHAR(150)
, [RowCount] INT
, [Reserved] NVARCHAR(50)
, [Data] NVARCHAR(50)
, [Index_Size] NVARCHAR(50)
, [UnUsed] NVARCHAR(50))
--inserting all records
INSERT INTO RowCounts([TableName], [RowCount],[Reserved],[Data],[Index_Size],[UnUsed])
-- "sp_MSforeachtable" System Procedure, 'sp_spaceused "?"' param to get records and resources used
EXEC sp_MSforeachtable 'sp_spaceused "?"'
-- selecting data and returning a table of data
SELECT [TableName], [RowCount],[Reserved],[Data],[Index_Size],[UnUsed]
FROM RowCounts
ORDER BY [TableName]
end
I have tested this code and it works fine on SQL Server 2014.
SELECT ( Schema_name(A.schema_id) + '.' + A.NAME ) AS TableName,
Sum(B.rows)AS RecordCount
FROM sys.objects A INNER JOIN sys.partitions B
ON A.object_id = B.object_id WHERE A.type = 'U'
GROUP BY A.schema_id,A.NAME ;
QUERY_PHOTO
QUERY_RESULT_PHOTO
Shnugo's answer is the ONLY one that works in Azure with Externa Tables. (1) Azure SQL doesn't support sp_MSforeachtable at all and (2) rows in sys.partitions for an External table is always 0.
select T.object_id, T.name, I.indid, I.rows
from Sys.tables T
left join Sys.sysindexes I
on (I.id = T.object_id and (indid =1 or indid =0 ))
where T.type='U'
Here indid=1 means a CLUSTERED index and indid=0 is a HEAP

Logs of executed Query in SQL Server

Database having 5 users
All users are running queries on database
we need to find what are the things all users doing like (Query , session_id,starttime , endtime,Database name,username , hostname )
we need to insert all the data into one table.
SELECT sdest.DatabaseName
,sdes.session_id
,sdes.[host_name]
,sdes.[program_name]
,sdes.client_interface_name
,sdes.login_name
,sdes.login_time
,sdes.nt_domain
,sdes.nt_user_name
,sdec.client_net_address
,sdec.local_net_address
,sdest.ObjName
,sdest.Query
FROM sys.dm_exec_sessions AS sdes
INNER JOIN sys.dm_exec_connections AS sdec ON sdec.session_id = sdes.session_id
CROSS APPLY (
SELECT db_name(dbid) AS DatabaseName
,object_id(objectid) AS ObjName
,ISNULL((
SELECT TEXT AS [processing-instruction(definition)]
FROM sys.dm_exec_sql_text(sdec.most_recent_sql_handle)
FOR XML PATH('')
,TYPE
), '') AS Query
FROM sys.dm_exec_sql_text(sdec.most_recent_sql_handle)
) sdest
where sdes.session_id <> ##SPID
--and sdes.nt_user_name = '' -- Put the username here !
ORDER BY sdec.session_id

Tsql queries into XML using a format

I've never used XML before. I have the following queries - that need to be output into XML file in a particular format
---- Show Server details
GO
SELECT
##servername as ServerName,
##version as Environment,
SERVERPROPERTY('productversion'),
SERVERPROPERTY ('InstanceName')
-- Show DB details
SELECT
name AS DBName ,
Collation_name as Collation,
User_access_Desc as UserAccess,
Compatibility_level AS CompatiblityLevel ,
state_desc as Status,
Recovery_model_desc as RecoveryModel
FROM sys.databases
ORDER BY Name
-- Sysadmin Roles
SELECT
p.name AS [Name],
r.type_desc,
r.is_disabled,
r.default_database_name
FROM
sys.server_principals r
INNER JOIN
sys.server_role_members m ON r.principal_id = m.role_principal_id
INNER JOIN
sys.server_principals p ON p.principal_id = m.member_principal_id
WHERE
r.type = 'R' and r.name = N'sysadmin'
-- Find all users associated with a database
DECLARE #DB_USers TABLE
(DBName sysname, UserName varchar(max), LoginType sysname, AssociatedRole varchar(max))--,create_date datetime,modify_date datetime)
INSERT #DB_USers
EXEC sp_MSforeachdb
use [?]
SELECT ''?'' AS DB_Name,
case prin.name when ''dbo'' then prin.name + '' (''+ (select SUSER_SNAME(owner_sid) from master.sys.databases where name =''?'') + '')'' else prin.name end AS UserName,
prin.type_desc AS LoginType,
isnull(USER_NAME(mem.role_principal_id),'''') AS AssociatedRole
FROM sys.database_principals prin
LEFT OUTER JOIN sys.database_role_members mem ON prin.principal_id=mem.member_principal_id
WHERE prin.sid IS NOT NULL and prin.sid NOT IN (0x00) and
prin.is_fixed_role <> 1 AND prin.name NOT LIKE ''##%'''
SELECT
DBName,UserName ,LoginType ,
STUFF(
(
SELECT ',' + CONVERT(VARCHAR(500), AssociatedRole)
FROM #DB_USers user2
WHERE
user1.DBName=user2.DBName AND user1.UserName=user2.UserName
FOR XML PATH('')
)
,1,1,'') AS Permissions_user
FROM #DB_USers user1
GROUP BY
DBName,UserName ,LoginType --,create_date ,modify_date
ORDER BY DBName,UserName
--List of all the jobs currently running on server
SELECT
job.job_id,
notify_level_email,
name,
enabled,
description,
step_name,
command,
server,
database_name
FROM
msdb.dbo.sysjobs job
INNER JOIN
msdb.dbo.sysjobsteps steps
ON
job.job_id = steps.job_id
-- Show details of extended stored procedures
SELECT * FROM master.sys.extended_procedures
I don't know where to start

SQL Server: Find Stored Procedures called within a procedure

I'm new to a company that makes heavy use of stored procedures (500+). To help learn the system, I was hoping there was an easy way to build a tree type list that shows all stored procedures in the system and which stored procedures they themselves call...thus creating a map of the stored procedures that could be executed. Is there an easy way to do this via a query in SQL Server? Is there a tool/utility that can do this?
For example, I want to see the following type of list without having to painstakingly try and follow the logic in each procedure and manually make a list.
build_house
-->pour_foundation
-->order_cement_truck
-->frame_house
-->hire_workers
-->buy_nails_and_hammers
-->wire_house
-->hire_electricians
-->check_certifications
-->test_wiring
The only thing I've found searching so far is:
http://www.codeproject.com/Articles/10019/Find-Stored-Procedures-called-within-a-procedure
To be clear, I'm looking to pass in / select a stored procedure name and have returned to me all of the stored procedures that it calls/uses.
#JackLock, I downloaded and installed SQL Search, but I don't see how this solves my problem. This tool aids in searching for stored procedures by name, or searching for text in stored procedures, but how does it help me automatically list out all stored procedures that are called from within a particular stored procedure? Maybe I'm missing something? For example, in my example above, I want the ability to run a system query or tool that returns me a list of the stored procedures that are called by whatever stored procedure name I pass it. So in the example, if I give the query or tool "build_house" it returns me the results in the example.
EDIT/UPDATE:
OK, I'd like to try and solve this with a query but need some help. I "think" what I want to do is query the sys.procedures to get the name of all the stored procedures in my system. Once I have them, I want to then pass them into the following query to determine how many stored procedures get called from it:
SELECT referenced_entity_name
FROM sys.dm_sql_referenced_entities (#ProcName, 'OBJECT')
Where #ProcName would get passed in for each row returned by the call to sys.procedures.
What is the most efficient way to do this in t-sql (2008)?
Thanks in advance,
Michael
You can Enter the particular Procedure name in the below code and check, you will get the particular procedure used in others or not
SELECT OBJECT_NAME(id)
FROM syscomments
WHERE [text] LIKE '% procedure_or_function_name %'
GROUP BY OBJECT_NAME(id)
You have not mentioned which version of SQL Server you are working on. But there is a free utility (actually SSMS addin) by RedGate called SQL Search.
I have it working on SSMS 2005,2008,R2 and 2012
It should solve your problem.
I know it's being along time since the question, however I think I found a workaround for this purpose and I want to share it, I've used an Aaron's Bertrand function to find a pattern within a text and this way of sorting,
Function :
CREATE FUNCTION dbo.FindPatternLocation
(
#string NVARCHAR(MAX),
#term NVARCHAR(255)
)
RETURNS TABLE
AS
RETURN
(
SELECT pos = Number - LEN(#term)
FROM
(
SELECT Number, Item = LTRIM(RTRIM(SUBSTRING(#string, Number, CHARINDEX(#term, #string + #term, Number) - Number)))
FROM (
SELECT ROW_NUMBER() OVER (ORDER BY [object_id])
FROM sys.all_columns) AS n(Number)
WHERE Number > 1 AND Number <= CONVERT(INT, LEN(#string)+1)
AND SUBSTRING(#term + #string, Number, LEN(#term)) = #term
)
AS y
);
Final query:
declare #object_name varchar(1000) = 'stored_procedure_name'
;
with cte as (
SELECT o.name AS parent_object_name
, SUBSTRING( m.definition, rs_fn.pos+4, CHARINDEX( ' ' , stuff( m.definition, 1, rs_fn.pos + 4 , '' )) ) as child_object
, cast(row_number()over(partition by o.object_id order by o.name) as varchar(max)) as [path]
, 0 as level
, row_number()over(partition by o.object_id order by o.name) / power(10.0,0) as x
FROM sys.sql_modules m
INNER JOIN sys.objects o ON m.object_id = o.object_id
cross apply (
select *
from dbo.FindPatternLocation( m.definition, 'EXEC ' ) as res
) as rs_fn
WHERE 1=1
and o.name like #object_name
union all
SELECT o.name AS parent_object_name
, SUBSTRING( m.definition, rs_fn.pos+4, CHARINDEX( ' ' , stuff( m.definition, 1, rs_fn.pos + 4 , '' )) ) as child_object
, [path] +'-'+ cast(row_number()over(partition by o.object_id order by o.name) as varchar(max))
, level+1
, c.x + row_number()over(partition by o.object_id order by o.name) / power(10.0,level+1)
FROM sys.sql_modules m
INNER JOIN sys.objects o ON m.object_id = o.object_id
inner join cte c on c.child_object = o.name
cross apply (
select *
from dbo.FindPatternLocation( m.definition, 'EXEC ' ) as res
) as rs_fn
WHERE 1=1
-- and o.name like #object_name
)
select * from cte
order by x
;
Feel free to use it as you wish.

Mysql query : If value(s) exist(s) in column, select just the value(s), else select all

Having a Mysql database, I have to select all ( TUser.Name, TAccount.Name ) pairs if TUser.name not exists in variable #UserNames.
If exists, I have to select only the ( TUser.Name, TAccount.Name ) pairs where TUser.name in #UserNames.
Something like the last line of the below query :
DECLARE #UserNames = "Alpha, Beta, Gama";
SELECT User.Name
, Account.Name
FROM TUser
, TAccount
, TUserAccount
WHERE TAccount.ID = TUserAccount.AccountID
AND TUserAccount.UserID = User.ID
-- please rewrite this line
AND TUser.Name IN ( IFNULL ( ( SELECT ID FROM TUser WHERE Name IN #UserNames ) , ( SELECT ID FROM TUser ) ) )
Thank you in advance !
You can't return mutually exclusive result sets with your criteria, without using a IF statement:
SELECT #sum := SUM(FIND_IN_SET(u.name, #UserNames))
FROM TUSER u
IF #sum > 0 THEN
SELECT u.name,
a.name
FROM TUSER u
JOIN TUSERACCOUNT ua ON ua.userid = u.id
JOIN TACCOUNT a ON a.id = ua.accountid
WHERE FIND_IN_SET(u.name, #UserNames) > 0
ELSE
SELECT u.name,
a.name
FROM TUSER u
JOIN TUSERACCOUNT ua ON ua.userid = u.id
JOIN TACCOUNT a ON a.id = ua.accountid
END IF;
You could make that work as a PreparedStatement, MySQL's dynamic SQL, but you still need to run a query to know if you need to return all or a subset.
References:
FIND_IN_SET
IF statements
SELECT
User.Name,
Account.Name
FROM TUser
JOIN TAccount
ON TAccount.Name = TUser.Name
JOIN TUserAccount
ON TUserAccount.AccountID = TAccount.ID
AND TUserAccount.UserID = TUser.ID
LEFT JOIN (
SELECT ID FROM TUser WHERE Name IN #UserNames
) AS UsersFound
ON TUser.ID = UsersFound.ID
Something like that? I haven't tested it though.
Ok, so this is what I would call 'hacks' rather than normall database querying, and I would disrecommend getting in the position you have to deal with in the first place. The right way to deal with a list of items like this is to have your application language parse it into a nice list you can use to build a normal, regular SQL IN list, like so:
TUser.Name IN ('Name1', 'Name2',...., 'NameX')
But anyway, if you want to stick to the original problem and your database is mysql, you can do it in a remotely sane way in a single query like this:
SET #UserNames := 'Alpha,Beta,Gama';
SELECT TUser.Name
, TAccount.Name
FROM TUser
INNER JOIN TUserAccount
ON TUser.ID = TUserAccount.UserID
INNER JOIN TAccount
ON TUserAccount.AccountID = TAccount.ID
WHERE FIND_IN_SET(TUser.Name, #UserNames)
OR (SELECT COUNT(*)
FROM TUser.Name
WHERE FIND_IN_SET(TUser.Name, #UserNames)) = 0
Note that I did change the input data somewhat - I don't have any spaces behind the commas. If that is unacceptable, simply change each occurrence of #UserNames in the query with REPLACE(#UserNames, ', ', ','). Also please note that it's performance down the drain as it is impossible to use any indexes on TUser.Name to filter for specific users.
I already mentioned that you really should make a proper IN list of your data. And you can do so directly in SQL too (dynamic SQL):
SET #UserNames := 'Alpha, Beta, Gama';
SET #stmt := CONCAT(
' SELECT TUser.Name'
,' , TAccount.Name'
,' FROM TUser'
,' INNER JOIN TUserAccount'
,' ON TUser.ID = TUserAccount.UserID'
,' INNER JOIN TAccount'
,' ON TUserAccount.AccountID = TAccount.ID'
,' WHERE TUser.Name IN (''', REPLACE(#UserNames, ', ', ''',''') , ''')'
,' OR (SELECT COUNT(*) '
,' FROM TUser.Name'
,' WHERE TUser.Name IN (''', REPLACE(#UserNames, ', ', ''',''') , ''')) = 0'
)
PREPARE stmt FROM #stmt;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
(Note that in this case, I could use the user name list with spaces unaltered)