I have a problem with getting this audit to work on my table.
I have a table with 197 columns, and i want to audit that. We have used this solution successfully before but the tables always had lesser columns.
The solution i have implemented is from this url at the bottom.
It just seems like it has something to do with that my table has so many columns. Does anyone have any input about this at all?
Pop Rivett SQL Server Audit
I have two ideas too many columns vs wrong ordinal position.
The code in link is from 2006. So it was written for SQL Server 2005. Based on COLUMNS_UPDATED():
The ORDINAL_POSITION column of the INFORMATION_SCHEMA.COLUMNS view is
not compatible with the bit pattern of columns returned by
COLUMNS_UPDATED.
To obtain a bit pattern compatible with
COLUMNS_UPDATED, reference the ColumnID property of the COLUMNPROPERTY
system function when you query the INFORMATION_SCHEMA.COLUMNS view.
CREATE TABLE tab(id INT, col1 INT, col2 INT, col3 INT,
col4 INT, col5 INT, col6 INT, col7 INT, col8 INT);
ALTER TABLE tab DROP COLUMN col1;
ALTER TABLE tab DROP COLUMN col5;
ALTER TABLE tab ADD col9 INT;
ALTER TABLE tab ADD col1 INT;
SELECT column_name,
ordinal_position,
COLUMNPROPERTY(OBJECT_ID(TABLE_SCHEMA + '.' + TABLE_NAME),
COLUMN_NAME, 'ColumnID') AS pos
FROM INFORMATION_SCHEMA.columns
WHERE [TABLE_NAME] like '%tab%'
ORDER BY ordinal_position;
SqlFiddleDemo
Output:
╔══════════════╦═══════════════════╦═════╗
║ column_name ║ ordinal_position ║ pos ║
╠══════════════╬═══════════════════╬═════╣
║ id ║ 1 ║ 1 ║
║ col2 ║ 2 ║ 3 ║
║ col3 ║ 3 ║ 4 ║
║ col4 ║ 4 ║ 5 ║
║ col6 ║ 5 ║ 7 ║
║ col7 ║ 6 ║ 8 ║
║ col8 ║ 7 ║ 9 ║
║ col9 ║ 8 ║ 10 ║
║ col1 ║ 9 ║ 11 ║
╚══════════════╩═══════════════════╩═════╝
Results:
ORIDINAL_POSITION -> no gaps, range 1-9
pos(aka ColumnId) -> gaps, range 1-11, 2 and 6 skipped
Alternatively to check if column has chagned or not you can use UPDATE:
UPDATE ( column )
Returns a Boolean value that indicates whether an INSERT or UPDATE
attempt was made on a specified column of a table or view. UPDATE() is
used anywhere inside the body of a Transact-SQL INSERT or UPDATE
trigger to test whether the trigger should execute certain actions.
Related
I am not completely new to SQL, but this time I am slow on the uptake.
For a data export, I have to select some user data including two JOINs. The data is not related, I just need both information in one export sheet.
I have created a example. The column groupname is from one JOIN and the column course from the other JOIN:
╔════╦═══════════╦══════════╦═══════════╦════════════╗
║ id ║ firstname ║ lastname ║ groupname ║ course ║
╠════╬═══════════╬══════════╬═══════════╬════════════╣
║ 1 ║ John ║ Doe ║ Manager ║ Management ║
║ 1 ║ John ║ Doe ║ CEO ║ Management ║
║ 1 ║ John ║ Doe ║ Manager ║ Logistics ║
║ 1 ║ John ║ Doe ║ CEO ║ Logistics ║
║ 1 ║ John ║ Doe ║ Manager ║ Leadership ║
║ 1 ║ John ║ Doe ║ CEO ║ Leadership ║
╚════╩═══════════╩══════════╩═══════════╩════════════╝
Due to the nature of JOINS; the groupname-column is duplicated now several times. But what I actually want, is something like this:
╔════╦═══════════╦══════════╦═══════════╦════════════╗
║ id ║ firstname ║ lastname ║ groupname ║ course ║
╠════╬═══════════╬══════════╬═══════════╬════════════╣
║ 1 ║ John ║ Doe ║ Manager ║ ║
║ 1 ║ John ║ Doe ║ CEO ║ ║
║ 1 ║ John ║ Doe ║ ║ Management ║
║ 1 ║ John ║ Doe ║ ║ Logistics ║
║ 1 ║ John ║ Doe ║ ║ Leadership ║
╚════╩═══════════╩══════════╩═══════════╩════════════╝
I guess, doing two SELECT-Statements consecutively would be the better option. Unfortunately, the original query with JOINS and where-Arguments has like 25 lines of code, so I would not like to duplicate it.
Is there a way to achieve my way of output more easily then doing for example a UNION with two long queries (see below for simple example in this case)?
SELECT u.[id]
,[firstname]
,[lastname]
,groupname
,'' AS course
FROM [dbo].[users] u
JOIN dbo.groups g ON u.id = g.userId
UNION ALL
SELECT u.[id]
,[firstname]
,[lastname]
,'' AS groupname
,course
FROM [dbo].[users] u
JOIN dbo.courses c ON u.id = c.userId
You could create a Table-Valued UDF with the query in question and then make a UNION ALL with the two UDFs
Pro:
- Much shorter Query:
SELECT * FROM [MY_UDF](Param 1, Param 2, ...)<br/>
UNION ALL<br/>
SELECT * FROM [MY_UDF](Different Param 1, Different Param 2, ...)
Con:
- You have to create the table valued UDF.
Otherwise: I think QUERY_1 UNION ALL QUERY_2 is probably the way to go.
I think their is a lot of way to solve your problem. I will give you the best way I can think of.
First Solution : Simply use a WITH not to repeat your common part query as follow:
WITH CommonPart (id, firstname, lastname)
AS
(
Select id, firstname, lastname
From [dbo].[users]
-- Eventually a filter ...
)
SELECT cp.*
, g.groupname
, '' AS course
FROM CommonPart cp
JOIN dbo.groups g ON cp.id = g.userId
UNION ALL
SELECT cp.*
,'' AS groupname
,c.course
FROM CommonPart cp
JOIN dbo.courses c ON cp.id = c.userId
Second Solution : You can Insert null values into courses and groups and use a simple LEFT JOIN. But I don't like this second solution.
EDIT: After Insert of null values, it would look like:
Select u.id,
u.firstname,
u.lastname,
g.groupname,
c.course
From [dbo].[users] u
Left Join [dbo].[groups] g ON g.userId = u.id
Left Join [dbo].[courses] c ON c.userId = u.id
Where (g.groupname IS NULL and c.course IS NOT NULL)
OR (g.groupname IS NOT NULL and c.course IS NULL)
I am using SQL Server 2008.
I have a table which has distinct numbers i.e int data type. Now I need a query which shows data in increasing order in the format cast(a as varchar)+'-'cast(b as varchar) i.e a-b where a is the smallest number which has yet not been shown and b is subsequently the next smallest number.
I know that SQL Server 2012 has the lead function which would make this question moot but how do I do that on sql server 2008?
CREATE TABLE #nr(nr INT); INSERT INTO #nr(nr)VALUES(9),(7),(1),(2),(25),(33),(10),(3);
SELECT
CAST(n_o.nr AS VARCHAR)+'-'+CAST((SELECT MIN(n_i.nr) FROM #nr AS n_i WHERE n_i.nr>n_o.nr) AS VARCHAR)
FROM
#nr AS n_o
WHERE
(SELECT MIN(n_i.nr) FROM #nr AS n_i WHERE n_i.nr>n_o.nr) IS NOT NULL
ORDER BY
nr;
DROP TABLE #nr;
Results in:
╔══════════════════╗
║ (No column name) ║
╠══════════════════╣
║ 1-2 ║
║ 2-3 ║
║ 3-7 ║
║ 7-9 ║
║ 9-10 ║
║ 10-25 ║
║ 25-33 ║
╚══════════════════╝
I have a table Users,
╔════╦═══════╦══════╗
║ Id ║ Name ║ Db ║
╠════╬═══════╬══════╣
║ 1 ║ Peter ║ DB1 ║
║ 2 ║ John ║ DB16 ║
║ 3 ║ Alex ║ DB23 ║
╚════╩═══════╩══════╝
and many databases that have the same structure (Same tables, same procedures, ...), so every database have a table named Project, and this is the structure of Project table,
╔════╦═════════╦═════════════╗
║ Id ║ Request ║ Information ║
╠════╬═════════╬═════════════╣
║ 1 ║ 126 ║ XB1 ║
║ 2 ║ 126 ║ D6 ║
║ 3 ║ 202 ║ BM-23 ║
╚════╩═════════╩═════════════╝
So, when I query a database :
SELECT count(distinct([Request])) as nbrRequests
FROM [SRV02].[DB1].[dbo].[Project]
I get this result :
╔═════════════╗
║ NbrRequests ║
╠═════════════╣
║ 2 ║
╚═════════════╝
Now, what I want is to "link"/"join" ... results from the table Users to this query, where the column Db in Users table is the name of my database, so I can get a result like this :
╔════╦═══════╦══════╦═════════════╗
║ Id ║ Name ║ Db ║ NbrRequests ║
╠════╬═══════╬══════╬═════════════╣
║ 1 ║ Peter ║ DB1 ║ 2 ║
║ 2 ║ John ║ DB16 ║ 3 ║
║ 3 ║ Alex ║ DB23 ║ 6 ║
╚════╩═══════╩══════╩═════════════╝
I'm trying with dynamic SQL, but no luck.
NB : Every user has only one database, and a database belong to only one user, it's one-to-one relationship
The way you can do it is with a UNION counting every specific database table and giving it a identification for the database, like this:
SELECT u.Id, u.Name, u.Db, dbCts.nbrRequests
FROM [Users] u INNER JOIN
(SELECT 'DB1' as db, count(distinct([Request])) as nbrRequests
FROM [SRV02].[DB1].[dbo].[Project]
UNION
SELECT 'DB16', count(distinct([Request])) as nbrRequests
FROM [SRV02].[DB16].[dbo].[Project]
UNION
SELECT 'DB23', count(distinct([Request])) as nbrRequests
FROM [SRV02].[DB23].[dbo].[Project]
) dbCts ON u.Db = dbCts.db
Don't forget to add the server and schema to the Users table I didn't because there is no such info on your question.
Also in order to do this, your connected user must have privileges on all databases.
Dynamic SQL can be very tricky.
This example builds the select query from the users table. The variable #Query is incremented for each line returned by the Users table. Each row returns a query that joins the local users table to the projects table in a remote db. The results of each query are UNIONED together.
Example
-- Wil holds our dynamic query.
DECLARE #Query NVARCHAR(MAX) = '';
-- Builds our dynamic statement.
SELECT
#Query =
#Query
+ CASE WHEN LEN(#Query) > 0 THEN ' UNION ALL ' ELSE '' END
+ 'SELECT u.Id, u.Name, u.Db, COUNT(DISTINCT p.Request) AS NbrRequest '
+ 'FROM [SVR02].' + QUOTENAME(DB) + 'dbo.Project AS p INNER JOIN Users u ON u.Db= p.Db '
+ 'GROUP BY u.Id, u.Name, u.Db'
FROM
Users
;
-- Executes the dynamic statement.
EXECUTE (#Query);
This example uses QUOTENAME to help avoid SQL injection attacks.
Combining these 2 answers https://stackoverflow.com/a/35795690/1460399 and https://stackoverflow.com/a/35795189/1460399, I got this solution :
DECLARE #Query NVARCHAR(MAX)= 'SELECT u.Id, u.Name, u.Db, dbCts.nbrRequests FROM [Users] u INNER JOIN (';
DECLARE #QueryLength INT= LEN(#Query);
SELECT #Query = #Query
+CASE WHEN LEN(#Query) > #QueryLength THEN ' UNION ' ELSE '' END
+'SELECT '''+Db+''' as db, count(distinct(Request)) as nbrRequests FROM [SRV02].'+Db+'[Project]'
FROM Users;
SET #Query = #Query+') dbCts ON u.Db = dbCts.db';
EXECUTE (#Query);
I have a full text search catalog and index set up on my table.
I first tried to run the query below with only one row in the table and it worked by returning the row I was looking based on the string 'test'. But when I placed a second row into the table it still only returned one row, not two rows. I'm trying to get it to return more then one row and I'm not sure why it's not. Any help would be appreciated.
Query's attempted
SELECT *
FROM dbo.Gifts
WHERE CONTAINS(Name, 'test')
SELECT *
FROM dbo.Gifts
WHERE CONTAINS(Name, '"test gift"')
SELECT *
FROM dbo.Gifts
WHERE FREETEXT (Name, 'test gift')
SELECT *
FROM dbo.Gifts
WHERE FREETEXT(Name, 'test')
and here are the two rows in the table
╔════════╦════════════╦════════╦════════╦═════════╦══════════════╦═══════════╗
║ GiftId ║ Name ║ Rating ║ Status ║ OwnerId ║ Availability ║ Thumbnail ║
╠════════╬════════════╬════════╬════════╬═════════╬══════════════╬═══════════╣
║ 1 ║ test gift ║ 5 ║ 0 ║ 1 ║ 0 ║ NULL ║
║ 2 ║ test gift1 ║ 2 ║ 0 ║ 1 ║ 0 ║ NULL ║
╚════════╩════════════╩════════╩════════╩═════════╩══════════════╩═══════════╝
Only the first row is being returned with giftid = 1 and I want both rows to be returned.
Just a quick question. I have table like this:
╔══════╦═════════════╗
║ id ║ date ║
╠══════╬═════════════╣
║ 90 ║ 2100-01-01 ║
║ 90 ║ 2100-01-01 ║
║ 91 ║ 2100-01-01 ║
║ 91 ║ null ║
║ 92 ║ 2100-01-01 ║
║ 92 ║ 2100-01-01 ║
║ 92 ║ null ║
║ 93 ║ 2100-01-01 ║
╚══════╩═════════════╝
I want to write a query that will retrieve only those IDs that have a date higher than current date in ALL of its rows. I that case:
╔════╗
║ id ║
╠════╣
║ 90 ║
║ 93 ║
╚════╝
Thanks!
Try this:
SELECT distinct id
FROM table1
WHERE id NOT IN (SELECT id
FROM table1
WHERE isnull(date,'') < Getdate())
Take a look at a working example on SQL Fiddle
So you want to find all ID's where all dates are in the future? I would use NOT EXISTS:
SELECT distinct(id)
FROM table1 t1
WHERE NOT EXISTS
(
SELECT 1 FROM table1 t2
WHERE t2.id = t1.id
AND ISNULL(t2.date, GETDATE()) <= GetDate()
)
Demo
The idea is, to create a subquery which returns those same ids, where the minimum date (within the same ids) is bigger than the current date. I like to use the outer SQL too, because as soon as you want to add columns to the output, you have to create it anyway if you do not want to retouch the group by.
SELECT id
FROM tab
WHERE id in
(SELECT id
FROM tab
GROUP BY id
HAVING MIN(IFNULL(date, GETDATE())) > GETDATE())
GROUP BY id