SQL Server query - loop question - sql

I'm trying to create a query that would generate a cross-check table with about 40 custom columns that show Y or N. Right now I have
SELECT DISTINCT [Company],
[Option1],
[Option2],
[Option3],
CASE
WHEN [Table1].[ID1] IN (SELECT ID2 FROM Table2 WHERE Variable = 1 AND Bit = 1) THEN
'Y'
ELSE 'N'
END AS 'CustomColumn1:',
CASE
WHEN [Table1].[ID1] IN (SELECT ID2 FROM Table2 WHERE Variable = 2 AND Bit = 1) THEN
'Y'
ELSE 'N'
END AS 'CustomColumn1:',
CASE
WHEN [Table1].[ID1] IN (SELECT ID2 FROM Table2 WHERE Variable = 3 AND Bit = 1) THEN
'Y'
ELSE 'N'
END AS 'CustomColumn1:',
.............
-- REPEAT ANOTHER 40 times
FROM [Table1]
WHERE [Table1].[OtherCondition] = 'True'
ORDER BY [Company]
So my question is, how do I create a loop (while? for?) that will loop on variable and assign Y or N to the row based on the condition, rather than creating 40+ Case statements?

You couldn't use a loop, but you could create a stored procedure/function to perform the sub-select and case expression and call that 40 times.
Also, you could improve performance of the sub-select by changing it to
SELECT 1 FROM Table2 WHERE EXISTS [Table2].[ID2] = [Table1.ID1] AND Variable = 3 AND Bit = 1

A loop (that is, iterating through a cursor) works on rows, not columns. You will still have to have 40 expressions, one for each column, and the performance will be terrible.
Let SQL Server do its job. And do your bit by telling exactly what you need and creating proper indices. That is, replace
CASE WHEN [Table1].[ID1] IN (SELECT ID2 FROM Table2 WHERE Variable = 2 AND Bit = 1)
with
CASE WHEN EXISTS (SELECT 0 FROM Table2 WHERE ID2 = [Table1].[ID1] AND Variable = 2 AND Bit = 1)

If the output is so vastly different than the schema, there is a question as to whether the schema properly models the business requirements. That said, I would recommend just writing the SQL. You can simplify the SQL like so:
Select Company
, Option1, Option2, Option3
, Case When T2.Variable = 1 Then 'Y' Else 'N' End As CustomCol1
, Case When T2.Variable = 2 Then 'Y' Else 'N' End As CustomCol2
, Case When T2.Variable = 3 Then 'Y' Else 'N' End As CustomCol3
, Case When T2.Variable = 4 Then 'Y' Else 'N' End As CustomCol4
...
From Table1 As T1
Left Join Table2 As T2
On T2.ID2 = T1.ID
And T2.Bit = 1
Where T1.OtherCondition = 'True'
Group By T1.Company
Order By T1.Company
If you want to write something that can help you auto-gen those Case statements (and you are using SQL Server 2005+), you could do something like:
With Numbers As
(
Select 0 As Value
Union All
Select Value + 1
From Numbers
Where Value < 41
)
Select ', Case When T2.Variable = ' + Cast(N.Value As varchar(10)) + ' Then ''Y'' Else ''N'' End As CustomCol' + Cast(N.Value As varchar(10))
From Numbers As N
You would run the query and copy and paste the results into your procedure or code.

One way could have been to use Pivot statement, which is in MS SQL 2005+. But even in that you have to put 1 ... 40 hardcoded columns in pivot statement.
Other way i can think of is to create dynamic SQL, but it is not so much recommended, So what we can do is we can create a dynamic sql query by running a while loop on table and can create the big sql and then we can execute it by using sp_execute. So steps would be.
int #loopVar
SET #loopVar = 0
int #rowCount
varchar #SQL
SET #SQl = ''
Select #rowcount = Count(ID2) from Table2
WHILE(#loopVar <= #rowCount)
BEGIN
// create ur SQL here
END
sp_execute(#SQL)

Related

Create and use column in create statement same time of table

I want to create a table with fetch the data from source table.
I can do it using alter and update function but I don't want to do it that way.
I have to create two columns and use 1 column(c1) in other as case statement in second(c2) column.
insert into table t
(select
a,b, case when d>f then 1 else 0 end as c1,
case when c1=1 then "yes" else null end as c2,
from base_temp
where a>b
)
If the question is on how to create a table and populate it with the results of some SELECT statement, then look at the CREATE TABLE as-result-table syntax.
You may use AS (full-select) WITH DATA syntax to create a new table with metadata based on the query provided and insert the result of this query into this table at the same time.
If the question is on how to use column aliases in expressions, then refer to the Expressions refer to column aliases topic.
You must have the SQL_COMPAT global variable set to the 'NPS' value in your session to make it work.
You may set it with the SET SQL_COMPAT = 'NPS' statement before running the corresponding SELECT statement.
SET SQL_COMPAT='NPS';
SELECT
TABSCHEMA, TABNAME
, CASE WHEN COLCOUNT > 20 THEN 1 ELSE 0 END AS C1
, CASE WHEN C1 = 1 THEN 'YES' ELSE NULL END AS C2
FROM SYSCAT.TABLES
FETCH FIRST 5 ROWS ONLY;
If you can't or don't want to set this variable, you may use sub-select to achieve the same:
SELECT
TABSCHEMA, TABNAME
, C1
, CASE WHEN C1 = 1 THEN 'YES' ELSE NULL END AS C2
FROM
(
SELECT
TABSCHEMA, TABNAME
, CASE WHEN COLCOUNT > 20 THEN 1 ELSE 0 END AS C1
FROM SYSCAT.TABLES
FETCH FIRST 5 ROWS ONLY
);

SQL - Capture result of where comparison as ad hoc column

I've got a SQL question. I want to use case logic to determine the value of an ad hoc column based on the result of comparisons done in the where clause. Simplified, I want to do something like this:
select t.id,
(case
when cond1 then "lbl1" + t2.v1
when cond2 then "lbl2" + t2.v1)
from
tbl1 as t left join tbl2 as t2
where
( cond1 || cond2 )
The problem is I don't want to recompute cond1 and cond2 in the select clause, as they're expensive.
How can I get this result?
Thanks,
frood
This was written in MS SQL 2008, not sure if it'll work in other RDBMS with the same code, but hopefully it gives you an idea of what might work. it would have to be iterated for each record, but overall would have to run each comparison once instead of twice for each record.
declare #cond1 as bit
declare #cond2 as bit
if 1=1 -- substitute with condition 1
set #cond1 = 1
ELSE
set #cond1 = 0
if 1=0 -- substitute with conditon 2
set #cond2 = 1
ELSE
set #cond2 = 0
SELECT
CASE WHEN #Cond1 = 1 THEN 'Condition 1 met'
WHEN #Cond2 = 1 THEN 'Condition 2 met'
END
WHERE #cond1 = 1 OR #cond2 = 1

Sum on multiple columns with nullable values

I have to edit a stored procedure who has to return the sums of three columns having nullable values. If there is a null value, I need to cast it to 0
Here is a screenshot of data :
And here is the originial request using the first column only :
SELECT SUM(reglProj.Montant) /* SUM of 'Montant', 'FraisMagasing', 'FraisVendeur' instead */ AS SommeReglement
FROM Projet.LigneEcheancierProjet ligne
INNER JOIN Projet.ReglementProjetEcheance reglProj ON reglProj.LigneEcheancierProjetId = ligne.LigneEcheancierProjetId
....
Do you have some best practices using the sum and case conditions in T-SQL ?
--ANSI standard
SELECT SUM(COALESCE(col1,0)) + SUM(COALESCE(col2,0)) + SUM(COALESCE(col3,0))
--SQL Server Style
SELECT SUM(ISNULL(col1,0)) + SUM(ISNULL(col2,0)) + SUM(ISNULL(col3,0))
--The one wthout functions. It will work the same as previous OR FASTER.
SELECT SUM(CASE WHEN col1 IS NULL THEN 0 ELSE col1 END) + SUM(CASE WHEN col2 IS NULL THEN 0 ELSE col2 END) + SUM(CASE WHEN col3 IS NULL THEN 0 ELSE col3 END)
Choose one for yourself.
OR you might need following (if you want to add sums by row):
--ANSI standard
SELECT SUM(COALESCE(col1,0) +COALESCE(col2,0) + COALESCE(col3,0))
--SQL Server Style
SELECT SUM(ISNULL(col1,0)+ ISNULL(col2,0) + ISNULL(col3,0))
--The one wthout functions. It will work the same as previous OR FASTER.
SELECT SUM(CASE WHEN col1 IS NULL THEN 0 ELSE col1 END + CASE WHEN col2 IS NULL THEN 0 ELSE col2 END + CASE WHEN col3 IS NULL THEN 0 ELSE col3 END)
In Sql Server, (and probably in most if not all relational databases) the SUM Aggregation function ignores null values by default, so there really is no need to use coalesce or isnull inside it.
If you want the sum of all 3 columns for every single row, then you need to use isnull:
SELECT ISNULL(reglProj.Montant,0) +
ISNULL(reglProj.FraisMagasing ,0) +
ISNULL(reglProj.FraisVendeur,0)
FROM Projet.LigneEcheancierProjet ligne
INNER JOIN Projet.ReglementProjetEcheance reglProj
ON reglProj.LigneEcheancierProjetId = ligne.LigneEcheancierProjetId
If you need the aggregated sum of all 3 columns you can simply do it like this:
SELECT ISNULL(SUM(reglProj.Montant), 0) +
ISNULL(SUM(reglProj.FraisMagasing), 0) +
ISNULL(SUM(reglProj.FraisVendeur), 0)
FROM Projet.LigneEcheancierProjet ligne
INNER JOIN Projet.ReglementProjetEcheance reglProj
ON reglProj.LigneEcheancierProjetId = ligne.LigneEcheancierProjetId
It seems you are looking for ISNULL actually
SELECT SUM( ISNULL(reglProj.Montant,0) + ISNULL(FraisMagasing,0)+ ISNULL(FraisVendeur,0)) AS SommeReglement
FROM Projet.LigneEcheancierProjet ligne
INNER JOIN Projet.ReglementProjetEcheance reglProj ON reglProj.LigneEcheancierProjetId = ligne.LigneEcheancierProjetId

Sorting based on multiple conditions

I am doing a exercise
I'll fire a query on the DB and get some 500 results. Now i want to sort this list based on some conditions and present the sorted list in client side.
I am using Java/Java EE and MySQL server 5.5
Conditions are like this,
Example: Consider a table having listed with cars
So, i ll fire a query on the table and it will list some 500 cars. now i want to sort this list based on user criteria.
conditions are age of car, colour of car and facilities of cars. List should be sorted like this
First appears the list of cars which satisfies all three conditions ie., same age as mentioned by end user, same colour and with all facilities user selected.
Second appears any 2 conditions satisfying cars list and one condition not satifying.
Third appears any one condition satisfying cars list and not the other two.
And finally appears the list of cars of which no conditions are satisfied.
How can i achieve this. I have searched in google, asked in irc channels regarding this. Couldn't get any help.
I have tried using RANK function by defining the CASES and finally order by RANK. It works for me while the conditions fields (columns) are of same table. In my case the fields are from a parent table as well as its child tables which has many to one relationship with its parent. Like in this example, age and color of the cars are stored in parent table and facilities that cars has are stored in another table. I tried doing the same using inner join, but no luck.
I tried something like this:
Query:
select distinct t0.id,t0.name,t0.price,
CASE
WHEN
t1.age='2' AND t1.colour='Red' AND t2.facilities_id=9 THEN 1
WHEN
t1.age='2' AND t1.colour='Red' AND t2.facilities_id!=9 THEN 2
WHEN
t1.age='2' AND t1.colour!='Red' AND t2.facilities_id=9 THEN 3
WHEN
t1.age!='2' AND t1.colour='Red' AND t2.facilities_id=9 THEN 4
WHEN
t1.age!='2' AND t1.colour='Red' AND t2.facilities_id!=9 THEN 5
WHEN
t1.age='2' AND t1.colour!='Red' AND t2.facilities_id!=9 THEN 6
WHEN *
t1.age!='2' AND t1.colour!='Red' AND t2.facilities_id=9 THEN 7
ELSE 8
END as pre_status
from cars_listing t0
inner join
cars_listing_details t1
on t0.id=t1.mg_listing_id
inner join
cars_facilities_listing t2
on t1.cars_listing_id=t2.listing_id
where t0.type='new_cars'
order by pre_status
Thanks in advance for helping.
try ordering by something like...
order by
case when first_condition then 1 else 0 end
+ case when second_condition then 1 else 0 end
+ case when third_condition then 1 else 0 end DESC
select distinct
t0.id,
t0.name,
t0.price,
case when t1.age = '2' then 1 else 0 end as MatchedAge,
case when t1.colour='Red' then 1 else 0 end as MatchedColor,
case when t2.facilities_id = 9 THEN 1 else 0 end as MatchedFacility
from
cars_listing t0
inner join cars_listing_details t1
on t0.id = t1.mg_listing_id
inner join cars_facilities_listing t2
on t1.cars_listing_id = t2.listing_id
where
t0.type = 'new_cars'
order by
case when t1.age = '2' then 1 else 0 end
+ case when t1.colour='Red' then 1 else 0 end
+ case when t2.facilities_id = 9 THEN 1 else 0 end DESC
If one field is a higher priority -- such as a red car, you could even give that more weight than the other in the order by... So a Red car at Facility 5 would show before a Blue car at facility 9 just by changing the order by to something like
order by
case when t1.age = '2' then 1 else 0 end
+ case when t1.colour='Red' then 5 else 0 end <-- applyi higher Wgt to color match vs other criteria
+ case when t2.facilities_id = 9 THEN 1 else 0 end DESC
Well, I have done Dynamic sql where condition in my project. It might help you. I have created a stored procedure for SELECT query. (I have done it in SQL Server 2008 R2). Tell me if you need more help.
USE [DATABASE_NAME]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[PROCEDURE_NAME]
#Id int = NULL,
#Requester varchar(20) = NULL,
#Suggester varchar(20) = NULL
AS
BEGIN
DECLARE #sql nvarchar(4000)
SELECT #sql='SELECT Id, Suggester, Requester from DATABASE_NAME.dbo.TABLE_NAME WHERE 1=1 '
If (#Id) IS NOT NULL
SELECT #sql=#sql + ' AND Id=(#Id) '
If (#Suggester) IS NOT NULL
SELECT #sql=#sql + ' AND Suggester like (#Suggester) '
If (#Requester) IS NOT NULL
SELECT #sql=#sql + ' AND Requester like (#Requester) '
EXEC sp_executesql #sql, N'#id int, #Requester varchar(20), #Suggester varchar(20)',
#Id, #Requester, #Suggester
END
GO
Here in this SP; Id,Requester,Suggester are field names.

SQL if select statement returns no rows then perform alternative select statement

Basically, what syntex would allow me to achieve the title statement?
If (select statement 1) returns 0 rows THEN (select statement 2) else (select statement 3)
So that the sql returns results from either statement 2 or 3
I've looked for a way to do this but nothing I've found so far seems to exactly address the if requirements.
IF EXISTS (SELECT field FROM table)
BEGIN
SELECT field FROM table2
END
ELSE
BEGIN
SELECT field FROM table3
END
Here you go...
IF ((select count(*) from table1)= 0)
BEGIN
Select * from table2
END
ELSE
BEGIN
SELECT * from table3
END
Sorry for the lack of feedback. Someone else in the office took an interest and came up with this:
select * from (
select *
, (SELECT Count(*)
FROM users
WHERE version_replace = 59 AND moderated = 1) AS Counter
FROM users WHERE version_replace = 59 AND moderated in (0,1)
) AS y
where Counter = 0 and Moderated = 0
or Counter > 0 and Moderated = 1
ORDER By ID DESC
Which does what I need.