TSQL Distinct List of departments based on current user - sql-server

I couldn't even think of how to phrase this properly for the title.
I have an SSRS report with a multi-valued parameter called Department.
If the user IS IN Department A..We want to default to all departments EXCEPT department A
If the user IS NOT IN Department A..We want to default to only their department
Department A will never be in the parameter list but being a member of department A impacts what you will see.
I know that I could resolve this with an ALL parameter option, but I would prefer the only parameter values to be valid department names
My parameter is populated with two datasets.
The first dataset has three options for valid departments: EUR, REM, LIFA
The second dataset only determines the current user's department and would populate the default. IF the current user's department is CS we want to select the other three departments as the default. If their department <> CS we want to default to only their department.
I thought the code below would work but the concatenated string is not an option in the first dataset so it cannot be the default option
SELECT DISTINCT
CASE
WHEN EmployeePracticeArea = 'CS'
THEN 'EUR, LIFA, REM'
ELSE EmployeePracticeArea
END AS 'EmployeePracticeArea'
FROM DimEmployee
WHERE
(EmployeePracticeArea <> '')
AND (UserLogin = #CurrentUser)
The problem with the case statement is that it tries to set a default value of EUR, LIFA, REM. This string does not exist in the 'options' list of values. The options are the three seperate strings EUR, LIFA, REM.
Case statements cannot return multiple values so I need to evaluate the current user's department and then return a list without it
Here is something which will generate the dataset for you
IF OBJECT_ID('tempdb..#TestData') IS NOT NULL
DROP TABLE #TestData;
WITH Data (EmployeePracticeArea) AS (
SELECT 'LIFA'
UNION
SELECT 'REM'
UNION
SELECT 'EUR'
UNION
SELECT 'CS'
)
SELECT * INTO #TestData FROM Data ;
The end result is like this:
User1 in the LIFA department has his parameter defaulted to just LIFA
User2 in the CS department has his parameter defaulted to EUR, LIFA, REM

DECLARE #t TABLE(Dept varchar(4))
DECLARE #CurrentUserDept varchar(4) = (SELECT EmployeePracticeArea FROM dimEmployee WHERE UserLogin = #CurrentUser)
IF #CurrentUserDept = 'CS'
BEGIN
INSERT INTO #t VALUES ('EUR'), ('LIFA'), ('REM')
END
ELSE
BEGIN
INSERT INTO #t SELECT #CurrentUserDept
END
SELECT * FROM #t

Related

How to loop with different values in T-SQL query?

I have some specific set of values that I want to filter on a column, I don't want to do an 'in' clause in SQL Server. I want to use loop to pass in different set of values each time.
For example if there is a name column in my data, and I want to run query 5 times with different filter value.
Please look at the loop query attached below.
DECLARE #cnt INT = 1;
WHILE #cnt < 94
BEGIN
SELECT Name, COUNT(*) AS Number_of_Names
FROM Table
WHERE name IN ('John')
AND value IS NOT NULL
GROUP BY Name
SET #cnt = #cnt + 1;
END;
I want to pass in different values under 'name' column at each loop like john in the case above, then mary in the next loop likewise based on set of values I pass in the variable like #values = John,Mary,Nicole,matt etc..
Considering the comments on your question, this should give you an idea on how to achieve a solution without using loops and still get all the names even when the name is not present on the table.
SELECT Name,
COUNT(value) AS Number_of_Names --Only count when value is not null
FROM (VALUES('John'), ('Mary'), ('Nicole'), ('Matt'))Names(name) --This can be replaced by a table-valued parameter or temp table.
LEFT JOIN Table t ON Names.name = t.name
--WHERE name IN ('John') /*No longer needed*/
--AND value IS NOT NULL /*Removed this because it would make the OUTER JOIN behave as an INNER JOIN*/
GROUP BY Name;

Using "in" in where condition with case

I have a sql table which has one column "type". It has values A,B,C,D.
I'm writing a stored procedure in which type is the in parameter #type.
#type can has either value 'A' or 'A,B,C' or 'ALL' based on user selection on screen. Which means user can select single,multiple or ALL options.
I need to filter data from my table with condition on column "type".
I want something similar to below
select * from maintable where
( case when #type ='ALL' then 1=1) else
type in (select data from SplitString(#type,',')) end)
I have written a split function which return values in a table format.
When ALL is selected then the whole table should be returned. When specific type(s) is selected, only those types should be returned.
I'm using sqlserver 2012.
Kindly help!!
Try this
SELECT *
FROM maintable
WHERE #type ='ALL' OR
(#type <>'ALL' AND TYPE IN (SELECT data FROM SplitString(#type,','))
You can do it like below :
if #type ='ALL'
set #type ='A,B,C,D'
select * from maintable where
type in (select data from SplitString(#type, ','))
You can use an IF:
IF #type ='ALL'
SELECT *
FROM MainTable
ELSE
SELECT *
FROM MainTable
WHERE Type IN (SELECT data FROM SplitString(#type,',') )

I want my stored procedure to only populate one instance of a name

I'm creating a stored procedure that populates two tables tblAirport and tblCountry. tblCountry gets its country names from tblAirport but I only want one instance of the country name to show up in `tblCountry. So far for my stored procedure I have this
DECLARE #PK INT = (SELECT PK FROM tblAirport WHERE strName = #strName)
IF #PK IS NULL
INSERT INTO tblAirport (ICAOCode,IATACode,strName,strCity,strCountry,degLat,minLat,secLat,Equator,degLong,minLong,secLong,Meridian,strElevation)
VALUES (#ICAOCode,#IATACode,#strName,#strCity,#strCountry,#degLat,#minLat,#secLat,#Equator,#degLong,#minLong,#secLong,#Meridian,#strElevation)
SET #PK = (SELECT PK FROM tblAirport WHERE strName = #strName);
IF EXISTS (SELECT * FROM tblCountry WHERE strCountry = #strCountry)
SET #strCountry = #strCountry + 'x'
INSERT INTO tblCountry (strCountry)
VALUES (#strCountry)
I tried using IF EXISTS (SELECT * FROM tblCountry WHERE strCountry = #strCountry)
SET #strCountry = #strCountry + 'x' just to show any duplicate countries but I don't know how to eliminate the duplicates from my table. I'm new to SQL and I've only learned the IF EXISTS function. Any suggestions would be great. Thank you!
This is how to handle a multiline IF ELSE (https://technet.microsoft.com/en-us/library/ms182717(v=sql.110).aspx)
IF NOT EXISTS (SELECT * FROM tblCountry WHERE strCountry = #strCountry)
BEGIN
INSERT INTO tblCountry (strCountry) VALUES (#strCountry)
END;
In general though, I'd be concerned about a procedure that uses the data to drive the possible values in a lookup list, especially something like countries that should probably be pre-defined up front. You'd hate for them to enter free-form duplicates that are really the same country with a slightly different spelling.

Copy records with dynamic column names

I have two tables with different columns in PostgreSQL 9.3:
CREATE TABLE person1(
NAME TEXT NOT NULL,
AGE INT NOT NULL
);
CREATE TABLE person2(
NAME TEXT NOT NULL,
AGE INT NOT NULL,
ADDRESS CHAR(50),
SALARY REAL
);
INSERT INTO person2 (Name, Age, ADDRESS, SALARY)
VALUES ('Piotr', 20, 'London', 80);
I would like to copy records from person2 to person1, but column names can change in program, so I would like to select joint column names in program. So I create an array containing the intersection of column names. Next I use a function: insert into .... select, but I get an error, when I pass the array variable to the function by name. Like this:
select column_name into name1 from information_schema.columns where table_name = 'person1';
select column_name into name2 from information_schema.columns where table_name = 'person2';
select * into cols from ( select * from name1 intersect select * from name2) as tmp;
-- Create array with name of columns
select array (select column_name::text from cols) into cols2;
CREATE OR REPLACE FUNCTION f_insert_these_columns(VARIADIC _cols text[])
RETURNS void AS
$func$
BEGIN
EXECUTE (
SELECT 'INSERT INTO person1 SELECT '
|| string_agg(quote_ident(col), ', ')
|| ' FROM person2'
FROM unnest(_cols) col
);
END
$func$ LANGUAGE plpgsql;
select * from cols2;
array
------------
{name,age}
(1 row)
SELECT f_insert_these_columns(VARIADIC cols2);
ERROR: column "cols2" does not exist
What's wrong here?
You seem to assume that SELECT INTO in SQL would assign a variable. But that is not so.
It creates a new table and its use is discouraged in Postgres. Use the superior CREATE TABLE AS instead. Not least, because the meaning of SELECT INTO inside plpgsql is different:
Combine two tables into a new one so that select rows from the other one are ignored
Concerning SQL variables:
User defined variables in PostgreSQL
Hence you cannot call the function like this:
SELECT f_insert_these_columns(VARIADIC cols2);
This would work:
SELECT f_insert_these_columns(VARIADIC (TABLE cols2 LIMIT 1));
Or cleaner:
SELECT f_insert_these_columns(VARIADIC array) -- "array" being the unfortunate column name
FROM cols2
LIMIT 1;
About the short TABLE syntax:
Is there a shortcut for SELECT * FROM?
Better solution
To copy all rows with columns sharing the same name between two tables:
CREATE OR REPLACE FUNCTION f_copy_rows_with_shared_cols(
IN _tbl1 regclass
, IN _tbl2 regclass
, OUT rows int
, OUT columns text)
LANGUAGE plpgsql AS
$func$
BEGIN
SELECT INTO columns -- proper use of SELECT INTO!
string_agg(quote_ident(attname), ', ')
FROM (
SELECT attname
FROM pg_attribute
WHERE attrelid IN (_tbl1, _tbl2)
AND NOT attisdropped -- no dropped (dead) columns
AND attnum > 0 -- no system columns
GROUP BY 1
HAVING count(*) = 2
) sub;
EXECUTE format('INSERT INTO %1$s(%2$s) SELECT %2$s FROM %3$s'
, _tbl1, columns, _tbl2);
GET DIAGNOSTICS rows = ROW_COUNT; -- return number of rows copied
END
$func$;
Call:
SELECT * FROM f_copy_rows_with_shared_cols('public.person2', 'public.person1');
Result:
rows | columns
-----+---------
3 | name, age
Major points
Note the proper use of SELECT INTO for assignment inside plpgsql.
Note the use of the data type regclass. This allows to use schema-qualified table names (optionally) and defends against SQL injection attempts:
Table name as a PostgreSQL function parameter
About GET DIAGNOSTICS:
Count rows affected by DELETE
About OUT parameters:
Returning from a function with OUT parameter
The manual about format().
Information schema vs. system catalogs.

How to view ALL COLUMNS (same column names from all table) from all tables starting with Letter A?

Is this possible? I am using ORACLE 10g.
For example: I have 50 tables name A01, A02, A03, A04.........A50.
And all these tables have the "SAME COLUMN NAME"
For example: name, age, location
(Note: The Column Names are the same but not the value in the columns).
In the END... I want to view all data from column: name, age, location FROM ALL tables starting with letter A.
(Note 2: All tables starting with letter A are NOT STATIC, they are dynamic meaning different changes could occur. Example: A01 to A10 could be deleted and A99 Could be added).
Sorry for not clarifying.
DECLARE
TYPE CurTyp IS REF CURSOR;
v_cursor CurTyp;
v_record A01%ROWTYPE;
v_stmt_str VARCHAR2(4000);
BEGIN
for rec in (
select table_name
from user_tables
where table_name like 'A%'
) loop
if v_stmt_str is not null then
v_stmt_str := v_stmt_str || ' union all ';
end if;
v_stmt_str := v_stmt_str || 'SELECT * FROM ' || rec.table_name;
end loop;
OPEN v_cursor FOR v_stmt_str;
LOOP
FETCH v_cursor INTO v_record;
EXIT WHEN v_cursor%NOTFOUND;
-- Read values v_record.name, v_record.age, v_record.location
-- Do something with them
END LOOP;
CLOSE v_cursor;
END;
As per my understanding if you want to view all column names of tables starting with A then try below
select column_name,table_name from user_tab_cols where table_name like 'A%';
If your requirement is something else then specify it clearly.
If understand you correctly and the number of tables is constant then you can create a VIEW once
CREATE VIEW vw_all
AS
SELECT name, age, location FROM A01
UNION ALL
SELECT name, age, location FROM A01
UNION ALL
...
SELECT name, age, location FROM A50
UNION ALL
And then use it
SELECT *
FROM vw_all
WHERE age < 35
ORDER BY name
This returns you all tables you need:
select table_name
from user_tables
where table_name like 'A__';
From this, you can build a dynamic sql statement as:
select listagg('select * from '||table_name,' union all ') within group(order by table_name)
from user_tables
where table_name like 'A__'
This returns actually an SQL statement which contains all tables and the unions:
select * from A01 union all select * from A02 union all select * from A03
And finally execute this via native dynamic sql. You can do that in PL/SQL, so you need a function:
create function getA
query varchar2(32000);
begin
select listagg('select * from '||table_name,' union all ') within group(order by table_name)
into query
from user_tables
where table_name like 'A__';
open res for query;
return res;
end;
Note that what you're doing manually is basically called partitioning, and Oracle has a super-great support already available for that out of the box. I.e. you can have something which looks like a super-huge table, but technically it is stored as a set of smaller tables (and smaller indexes), splitted by a partitioning criteria. For example, if you have millions of payment records, you may partition it by year, this way one physical table contains only a reasonable set of data. Still, you can freely select in this, and if you're hitting data from other partitions, Oracle takes care of pulling those in.

Resources