Select Columns based on value of another variable? - sql

Suppose I have this code:
DECLARE #IncludeDuplicateAddressIDs tinyint
-- Value of #IncludeDuplicateAddressIDs set here ! --
IF(#IncludeDuplicateAddressIDs = 1)
SELECT AddressIds FROM RTS.ADDRESSES
ELSE
SELECT DISTINCT(AddressIds) FROM RTS.ADDRESSES
Can I combine the two SELECT statements into one ? That is, using just one SELECT, I show either all AddressIDs, or just one of each ?

You can do it like this:
SELECT AddressIds FROM RTS.ADDRESSES WHERE #IncludeDuplicateAddressIDs = 1
UNION ALL
SELECT DISTINCT(AddressIds) FROM RTS.ADDRESSES WHERE #IncludeDuplicateAddressIDs <> 1
Since the WHERE clauses are mutually exclusive, only one of the UNION-ed queries would return some rows; the other query will return nothing.

If the table contains a unique column then you can use this option (assuming the unique column's name to be Id):
DECLARE #IncludeDuplicateAddressIDs tinyint = 1
SELECT AddressIds
FROM RTS.ADDRESSES
GROUP BY CASE WHEN #IncludeDuplicateAddressIDs = 1
THEN Id ELSE AddressIds END, AddressIds
Demo on SQLFiddle

You could include the number of duplicate addresses.
SELECT AddressIDs, count(*) as cnt
FROM RTS.ADDRESSES
GROUP BY AdressIDs

Try this one -
DECLARE #IncludeDuplicateAddressIDs TINYINT
SELECT #IncludeDuplicateAddressIDs = 0
SELECT t.AddressIds
FROM (
SELECT
AddressIds
, cnt = ROW_NUMBER() OVER (PARTITION BY AddressIds ORDER BY AddressIds)
FROM RTS.ADDRESSES
) t
WHERE #IncludeDuplicateAddressIDs = 1
OR (#IncludeDuplicateAddressIDs != 1 AND cnt = 1)
In this query, the UNION or DISTINCT construction is not used. Therefore, SQL can generate a more efficient query plan.

You can use sp_executesql dynamic sql to form your query as a string (and set as many columns as you want) and then execute it:
DECLARE #IncludeDuplicateAddressIDs tinyint
DECLARE #Columns varchar(500);
IF(#IncludeDuplicateAddressIDs = 1) SET #Columns='AddressIds'
ELSE SET #Columns='DISTINCT(AddressIds)'
EXECUTE sp_executesql N'SELECT ' + #Columns + 'AddressIds FROM RTS.ADDRESSES';

Related

SQL concatenation with ORDER BY clause

I want to concatenate a variable name into my SQL query for order by such as the following..
I basically don't wan to use dynamic sql and exec and sp_executesql.
DECLARE #Order VARCHAR(1000)
SET #Order = 'CO_ID DESC'
print #Order
select * From contact where co_username= 'sandeepshm' order by #Order+#Order
select * From contact where co_username= 'sandeepshm' order by CO_ID DESC
you can use Case statement but not variable
something like this:
declare #sortByCO_id bit
declare #sortbyCo_Username bit
select
*
from contact
order by
case when #sortByCO_id = 1 then CO_Id else NULL end DESC
,case when sortbyCo_Username = 1 then Co_Username else NULL end

SQL Grouping with condition

I want to sum rows in table. The algorithm is rather simple in theory but hard (at least for me) when I need to build a query.
Generally, I want to sum "values" of a "sub-group". Sub-group is defined as a range of elements starting with first row where type=0 and finishing with last row where type=1. the sub-group should contain only one (first) row with type=0.
The sample below presents correct (left) and incorrect (right) behavior.
I tried several approaches including grouping and partitioning. Unfortunately w/o any success. Anybody had similar problem?
I used MS SQL Server (so T-SQL 'magic' is allowed)
EDIT:
The results I want:
"ab",6
"cdef",20
"ghi",10
"kl",8
You can identify the groups by doing a cumulative sum of zeros. Then use aggregation or window functions.
Note that SQL tables represent unordered sets, so you need a column to specify the ordering. The code below assumes that this column is id.
select min(id), max(id), sum(value)
from (select t.*,
sum(case when type = 0 then 1 else 0 end) over (order by id) as grp
from t
) t
group by grp
order by min(id);
You can use window function with cumulative approach :
select t.*, sum(value) over (partition by grp)
from (select t.*, sum(case when type = 0 then 1 else 0 end) over (order by id) as grp
from table t
) t
where grp > 0;
Solution with a cursor and output-table.
As Gordon wrote it is not defined how the set will be ordered, so ID is also used here.
declare #output as table (
ID_sum nvarchar(max)
,value_sum int
)
DECLARE #ID as nvarchar(1)
,#value as int
,#type as int
,#ID_sum as nvarchar(max)
,#value_sum as int
,#last_type as int
DECLARE group_cursor CURSOR FOR
SELECT [ID],[value],[type]
FROM [t]
ORDER BY ID
OPEN group_cursor
FETCH NEXT FROM group_cursor
INTO #ID, #value,#type
WHILE ##FETCH_STATUS = 0
BEGIN
if (#last_type is null and #type = 0)
begin
set #ID_sum = #ID
set #value_sum = #value
end
if (#last_type in(0,1) and #type = 1)
begin
set #ID_sum += #ID
set #value_sum += #value
end
if (#last_type = 1 and #type = 0)
begin
insert into #output values (#ID_sum, #value_sum)
set #ID_sum = #ID
set #value_sum = #value
end
if (#last_type = 0 and #type = 0)
begin
set #ID_sum = #ID
set #value_sum = #value
end
set #last_type = #type
FETCH NEXT FROM group_cursor
INTO #ID, #value,#type
END
CLOSE group_cursor;
DEALLOCATE group_cursor;
if (#last_type = 1)
begin
insert into #output values (#ID_sum, #value_sum)
end
select *
from #output

How to store result of a select statement into a variable?

I tried with the below code:
DECLARE #rec_count int
Set #rec_count= select 1
but it shows error
"Incorrect syntax near Select".
Either:
set #rec_count = (select 1)
or
select #rec_count = 1
An example assigning the count from a table to variable:
set #rec_count = (select COUNT(*) from master..spt_values)
select #rec_count = COUNT(*) from master..spt_values
However, if you just want to assign a value to a variable you don't need any select statement:
set #rec_count = 1
or
declare #rec_count int = 1
To store the result of a select statement into a variable, see below
DECLARE #rec_count INT
SELECT #rec_count = 1
We can get more than one value from select statement as below
DECLARE #rec_count INT
DECLARE #date DATETIME
SELECT #rec_count = 1, #date = GETDATE()
If you're trying to get a record count, which looks like what you're trying to do, you can do this:
declare #rec_count int
select #rec_count = count(1) from [your_table] -- where some condition is met
Note: Use count(1) instead of count(*) as it's quicker to simply select a single column than all when getting a count.
Or if this count is the result of some inserts/updates/selects/deletes, you can use the ##ROWCOUNT, which:
Returns the number of rows affected by the last statement.
So if for example you are performing an update or select and want to know how many rows were affected, it will automatically be stored by SQL Server in the ##ROWCOUNT.
declare #rec_count int
-- some table change
update your_table
set col1 = 1
where col1 = 0
set #rec_count = ##ROWCOUNT
-- select ##ROWCOUNT <-- this would return the number of rows updated
DECLARE #rec_count int;
Set #rec_count = 1;

Scope of table when using with clause

Below is a piece of my stored proc.
I am getting error as invalid object MyCount, Please let me know where am going wrong
;With MyCount AS
(
Select DispatchToRegionId ,FolderNo, row_number() OVER(ORDER BY FolderNo DESC) as Row
from tblTransite where FolderNo = #VAL group by DispatchToRegionId,FolderNo
)
select #cnt = COUNT(*) from MyCount
if #cnt = 0
begin
set #InvalidFolderNo = #VAL
print 'cnt -' + cast(#cnt as varchar(max) ) + 'invalid folder - ' + cast(#InvalidFolderNo as varchar(max) )
return
end
select #Region =( Select top 1 DispatchToRegionId from MyCount
order by Row desc )
MSDN clearly states the following about the scope of a Common Table Expression (CTE):
A common table expression (CTE) can be thought of as a temporary result set that is defined within the execution scope of a single SELECT, INSERT, UPDATE, DELETE, or CREATE VIEW statement
Once you have run the first select query, you can no longer use the CTE for your next one. You may want to consider storing the data in a temporary table or table variable if you want to access it in multiple queries.
MyCount is available only for the first query after it. Try to select all you need in this one query:
;With MyCount AS
(
Select DispatchToRegionId ,FolderNo, row_number() OVER(ORDER BY FolderNo DESC) as Row
from tblTransite where FolderNo = #VAL group by DispatchToRegionId,FolderNo
)
SELECT
#cnt = (select COUNT(*) from MyCount),
#Region =( Select top 1 DispatchToRegionId from MyCount
order by Row desc )
if #cnt = 0
begin
set #InvalidFolderNo = #VAL
print 'cnt -' + cast(#cnt as varchar(max) ) + 'invalid folder - ' + cast(#InvalidFolderNo as varchar(max) )
return
end

Sql server Query : Conditional Select columns with top keyword

DECLARE #mode INT;
SELECT CASE
WHEN #mode = 0
THEN t.Column1,Count(t.Column2) as Column2
ELSE top 1 t.Column1,Count(t.Column2) as Column2
END
FROM Table1 t
--Where some list of parameters
Group by t.Column1,t.Column2
Please read the above sql statement carefully. I have requirement to evaluate the query by two modes without changing the body of the query ie. From,Where and Group clauses should be written only once and not to replicate them (each one) anywhere in the result query
if #mode = 0 then the above said columns should be returned, and
if #mode <> 0 then the "Top1" of records should be returned
Both select conditions use the same given set list of parameters.
When I run the above query am facing the error "Incorrect syntax near the keyword 'top'." because we could not use the top 1 keyword within conditional select statements and select condition's columns must be matched even with their datatypes.
I need to fix the above query without affecting logic of the query.
IF(#mode <= 0)
BEGIN
Select Column1,Count(t.Column2) as Column2
From Table1 t
Group by t.Column1,t.Column2
END
Else
BEGIN
Select top 1 *
From Table1 t
END
Or create a temporary table and load data using where condition
Create Table #Temp (Column1 datetype,Column2 datetype)
Insert Into #Temp (Column1,Column2)
Select Column1,Column2
From Table1 t
Where condition
IF(#mode <= 0)
BEGIN
Select Column1,Count(t.Column2) as Column2
From #Temp t
Group by t.Column1,t.Column2
END
Else
BEGIN
Select top 1 *
From #Temp t
END
BEGIN
DECLARE #mode INT;
SET #mode = 0;
IF #mode <= 0
SELECT t.Column1,count(t.Column2) as Column2 from Table_Name t
GROUP BY t.Column1,t.Column2
ELSE
SELECT TOP 1 t.Column1,count(t.Column2) as Column2 from Table_Name t
GROUP BY t.Column1,t.Column2
END
DECLARE #mode INT;
Set #mode = 1 --for Min set of records
--Set #mode = 100 --for Max / full set of records
SELECT TOP (#var) PERCENT * t.Column1 ,Count(t.Column2) AS Column2 FROM Table1 t
--Where some list of parameters
GROUP BY t.Column1 ,t.Column2