I have the following situation in Sql Server:
In a table we have a column that we fill with three possible values 1, 2 and 3. Let's suppose that 1 means yes, 2 means no and 3 means maybe.
Is there a way to select this column showing the values as yes, no and maybe instead of 1, 2 and 3?
Yes ..you can use CASE Expression to do that
select
case value
when 1 then 'yes'
when 2 then 'no'
when 3 then 'maybe'
end
from
table
You can use Case for this as shown below
SELECT
CASE test_column
WHEN 1THEN 'YES'
WHEN 2 THEN 'NO'
ELSE 'MAY BE'
END as test_op
FROM table1
Yes it is possible, you can use case which would be something like this
select
case when field = 1 then 'YES'
when field = 2 then 'NO' else 'MAYBE' end FieldName
from table
Note: any value other than 1 or 2 would be maybe, you can add another case for the number 3.
An alternative to the case/when statements seen in other answers is to create a reference table that contains the description of the values (1/2/3).
The biggest advantage of doing it this way is that if this is used in multiple places, you can update all of them at once.
The dbo.curValDesc table is the reference table you'd need to create/populate. Then your consuming queries can look like the one at the bottom.
create table dbo.existingData
(
rowID int
, curVal tinyint --the column with values of 1/2/3
)
insert into dbo.existingData
values (1, 1)
, (2, 2)
, (3, 3)
, (4, 3)
, (5, 2)
, (6, 1)
, (7, 1)
create table dbo.curValDesc
(
curVal tinyint
, curValDesc varchar(10)
)
insert into dbo.curValDesc
values (1, 'Yes')
, (2, 'No')
, (3, 'Maybe')
select ed.rowID
, cvd.curValDesc
from dbo.existingData as ed
inner join dbo.curValDesc as cvd on ed.curVal = cvd.curVal
Related
Source Table
Row_no
Flag
Curr_value
Prev_value
1
C
V1
NULL
2
P
V11
NULL
3
P
V12
NULL
4
C
V2
NULL
5
C
V31
NULL
6
P
V32
NULL
I have a scenario where i have to compare previous row value with current row value and update the Curr_value column based on a case condition and the curr_value and Prev_value should be derived for all the records in the table.
The condition used to derive Curr_value column is case when (Flag='C') then curr_value else Prev_value end and I’m using LAG function in MSSQL to get Previous value column.
OUTPUT
Row_no
Flag
Curr_value
Prev_value
1
C
V1
0
2
P
V1
V1
3
P
V1
V1
4
C
V2
V1
5
C
V3
V2
6
P
V3
V3
I tried implementing the same using While Loop but the execution time is very high. Please let me know if the same output can be achieved without using loops in MSSQL.
I set up the following script as a complete working example:
CREATE TABLE #tmpSampleData(
Row_no int NOT NULL PRIMARY KEY
,Flag varchar(1) NULL
,Curr_value varchar(3) NULL
);
INSERT INTO #tmpSampleData (Row_no,Flag,Curr_value)
VALUES (1, 'C', 'V1')
,(2, 'P', 'V1')
,(3, 'P', 'V1')
,(4, 'C', 'V2')
,(5, 'C', 'V3')
,(6, 'P', 'V3');
SELECT
sample_data.Row_no
,sample_data.Flag
,sample_data.Curr_value
,LAG(sample_data.Curr_value,1,'0') OVER(ORDER BY sample_data.Row_no) AS Prev_value
,CASE
WHEN sample_data.Flag = 'C'
THEN sample_data.Curr_value
ELSE LAG(sample_data.Curr_value,1,'0') OVER(ORDER BY sample_data.Row_no)
END AS CalculatedField
FROM
#tmpSampleData AS sample_data;
IF OBJECT_ID(N'tempdb..#tmpSampleData', N'U') IS NOT NULL
BEGIN
DROP TABLE #tmpSampleData;
END;
But I think the main part you are interested in here is just this:
CASE
WHEN sample_data.Flag = 'C'
THEN sample_data.Curr_value
ELSE LAG(sample_data.Curr_value,1,'0') OVER(ORDER BY sample_data.Row_no)
END AS CalculatedField
Simply add your condition of sample_data.Flag = 'C' and then either return Curr_value or the result of your `LAG
Just use Lag function...
Create table #temp
(
id int,
Fuel int,
dates date
)
Insert into #temp values(1,10,getdate())
Insert into #temp values(2,5,getdate()+1)
Insert into #temp values(3,18,getdate()+2)
Insert into #temp values(4,10,getdate()+3)
Insert into #temp values(5,8,getdate()+4)
Insert into #temp values(6,20,getdate()+5)
Insert into #temp values(7,10,getdate()+6)
Select * from (
SELECT id,
LAG(Fuel, 1) OVER (ORDER BY id ASC) AS previous_Fuel , Fuel, dates
FROM #temp )A where Isnull(previous_Fuel,0) < Fuel
This table is for the purpose of demo, but I have physical table whose values I need to insert into another table. there is no primary key in this table. The question I have is - Is the only way to get all the data in one SELECT statement using aggregate values (using SUM, AVG, etc.) and non-aggregate fields is listing all the not aggregate fields in the GROUP BY clause or is there some other way as well? What would be the impact of listing a large number of fields in the GROUP BY clause?
Here is the sample:
CREATE TABLE #SummaryData(
[Col_Name] varchar(20) not NULL,
[Col_Date] datetime NULL,
[ColC] [decimal](18, 4) NULL,
[ColD] [decimal](18, 4) NULL,
[ColE] [decimal](18, 4) NULL
)
INSERT INTO #SummaryData ([Col_Name],[Col_Date],[ColC],[ColD],[ColE])
VALUES ('BOA' ,'03/10/2017', 2.4507 ,33536.0000 ,0.0073)
INSERT INTO #SummaryData ([Col_Name],[Col_Date],[ColC],[ColD],[ColE])
VALUES ('BOA' , '03/11/2017' , 9.9419,47041.0000, 0.0088)
INSERT INTO #SummaryData ([Col_Name],[Col_Date],[ColC],[ColD],[ColE])
VALUES ('Merrill Lynch', '03/10/2017', 2.8152, 32371.0000, 0.0042)
INSERT INTO #SummaryData ([Col_Name],[Col_Date],[ColC],[ColD],[ColE])
VALUES ('Merrill Lynch', '03/11/2017', 9.9333, 35671.0000, 0.0444)
--NOTE: Next SELECT will be used to INSERT data into another table, so I need all fields
SELECT [Col_Name],[Col_Date],[ColC],
CASE WHEN SUM([ColE]) > 0 THEN SUM([ColD])/SUM([ColE]) ELSE 0 END AS SomeVal , [ColE]
FROM #SummaryData
GROUP BY [Col_Name],[Col_Date],[ColE],[ColC]
If I do not include ColE and ColC in the GROUP BY clause I get:
Msg 8120, Level 16, State 1, Line 21
Column '#SummaryData.Col_Date' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
Whenever you use an aggregate function, all non-aggregate values in your SELECT statement need to appear in your group by statement. If you want to insert aggregate values then you need to use the group by. With that said, why do you need to use the SUM function? This would only be needed if you had duplicate entries you were consolidating. The below query avoids the SUM and thus does not need a group by.
SELECT [Col_Name],[Col_Date],[ColC],
CASE WHEN [ColE] > 0 THEN [ColD]/[ColE] ELSE 0 END AS SomeVal , [ColE]
FROM #SummaryData
If you want to see all of the records, you can't use a GROUP BY at all. If you need intermediate values such as SUM(ColE) and SUM(ColD) from the whole table, you can calculate them and put them into a variable. Then you can use the variables however you want to.
DECLARE #SumE DECIMAL(18, 4);
SELECT #SumE = SUM(ColE) FROM #SummaryData
That's totally correct, Group by has all non aggregate functions.
but Why ?
Simple Demo:-
create table emp (empid int , departmentName varchar(15))
go
insert into emp values (1 , 'HR')
insert into emp values (2 , 'HR')
insert into emp values (3 , 'HR')
insert into emp values (4 , 'Sales')
insert into emp values (5 , 'Sales')
insert into emp values (7 , 'Developemnet')
insert into emp values (8 , 'Developemnet')
insert into emp values (9 , 'Developemnet')
insert into emp values (10 , 'Developemnet')
insert into emp values (11 , 'Developemnet')
The Desired Result is:-
countEmpID departmentName
5 Developemnet
3 HR
2 Sales
so for achieving that, you MUST select count (empid) & departmentName then Group by with non aggregate functions (departmentName) because this is way to making groups via next code:-
select count (empid) countEmpID, departmentName
from emp
group by departmentName
and this way if you didn't put non aggragate functions in group by, the next error will be raised:-
Msg 8120, Level 16, State 1, Line 15 Column 'emp.departmentName' is
invalid in the select list because it is not contained in either an
aggregate function or the GROUP BY clause.
Hope it helps.
I have a table with ProducerName and ProducerRequirementsListID
Each ProducerName supposed to have 3 requirements(1,2,3), but some of them might missing at least one of them: either 1, 2 or 3.
So how can I catch unique ProcucerName that missing AT LEAST ONE of the ProducerRequirementsListID?
Ideally would be create 3 new columns Req_1,Req_2 Req_3 and for each unique ProducerName display TRUE or FALSE
Something like that:
Should I be using WHILE loop for operations like that?
It's not clear that you're actually trying to get a pivot or you are looking only for ProducerName that are missing requirements. Assuming the latter, then all you need to do is group by and filter by less than 3. For example:
SELECT ProducerName
FROM Table
GROUP BY ProducerName
HAVING COUNT(*) < 3
I would say a pivot would be what you are looking for. Simple self extracting example. While Loops while useful should not be done as a first resort in SQL as it is result set based language and performance could be bad over time when doing in a while loop what could be done with a pivot.
DECLARE #People TABLE (PersonName VARCHAR(128), Ord INT);
INSERT INTO #People (PersonName, Ord) VALUES ('Brett', 1), ('Brett', 2), ('Brett', 3), ('Emily', 1), ('Emily', 2);
SELECT
PersonName
, [1] as FirstValue
, [2] as SecondValue
, [3] as ThirdValue
From #People
PIVOT ( Count(Ord) FOR Ord IN ([1],[2],[3])) AS piv
You can use a quick case statement....
select
ProducerName
,case when ProducerRequirementListID = 1 then 'TRUE' else 'FALSE' end as Req1
,case when ProducerRequirementListID = 2 then 'TRUE' else 'FALSE' end as Req2
,case when ProducerRequirementListID = 3 then 'TRUE' else 'FALSE' end as Req3
from ProducerName
Here is a way to get just a list of the producers who are missing one...
select ProducerName
from ProducerTable
group by ProducerName
having count(ProducerName) < 3
Here's a better way, using test data from django, to find which one is missing...
select
ProducerName
,case
when sum(ProducerRequirementListID) = 3 then 3
when sum(ProducerRequirementListID) = 4 then 2
when sum(ProducerRequirementListID) = 5 then 1
when sum(ProducerRequirementListID) = 6 then NULL
end as MissingReq
from ProducerTable
group by ProducerName
TEST DATA
DECLARE #People TABLE (PersonName VARCHAR(128), Ord INT);
INSERT INTO #People (PersonName, Ord) VALUES ('Brett', 1), ('Brett', 2), ('Brett', 3), ('Emily', 1), ('Emily', 2), ('Jake', 1), ('Jake', 3);
SELECT
PersonName,
case
when sum(Ord) = 4 then 2
when sum(Ord) = 3 then 3
when sum(Ord) = 5 then 1
when sum(Ord) = 6 then NULL
end as MissingReq
from #People
group by PersonName
I have a table in SQL Server:
Unique ITEM_ID are part of a group (GROUP_NUMBER). IS_ACTIVE and IS_LAST are either 1 - true , or 0 - false.
What I want to do:
I want to go through this table and for every active ITEM_ID (IS_ACTIVE = 1) that is the ONLY active ITEM_ID in it's group (GROUP_NUMBER) I want to make that row's IS_LAST is set to 1.
So for example, in the table above, the row for ITEM_ID = 6, I want IS_LAST to be 1
I am not sure how to do this as I am not that versed in SQL. I am trying to use a partition by command to maybe split each group up but doing the check to see if an ITEM_ID is the only active in its group seems challenging.
Any help or guidance here is appreciated.
It should be noted that I do not want to do an update or change the actual table in any way, just design a query that can do the changing and spit out an altered version of that table.
Here is solution:
DECLARE #t TABLE(ITEM_ID INT, GROUP_NUMBER INT, IS_ACTIVE BIT, IS_LAST bit)
INSERT INTO #t VALUES
(1, 1, 1, 0),
(2, 1, 1, 0),
(3, 2, 0, 0),
(4, 2, 0, 0),
(5, 2, 0, 0),
(6, 3, 1, 0),
(7, 3, 0, 0)
SELECT t1.ITEM_ID,
t1.GROUP_NUMBER,
t1.IS_ACTIVE,
CASE WHEN t1.IS_ACTIVE = 1 AND
NOT EXISTS(SELECT * FROM #t t2
WHERE t2.IS_ACTIVE = 1 AND t1.GROUP_NUMBER = t2.GROUP_NUMBER AND
t1.ITEM_ID <> t2.ITEM_ID)
THEN 1 ELSE 0 END AS IS_LAST
FROM #t t1
The following query returns the ITEM_ID's where only one is active in the group:
select ITEM_ID from MyTable M
where IS_ACTIVE = 1 and
not exists (select null
from MyTable N where n.IS_ACTIVE = 1 and
M.GROUP_NUMBER = N.GROUP_NUMBER and M.ITEM_ID <> N.ITEM_ID)
You can then left join this query with MyTable. Something like:
select *
from MyTable A left join (<query above>) B on A.ITEM_ID = B.ITEM_ID
If B.ITEM_ID is not Null then IS_LAST = 1.
I think you're on the right tracks with Partition By. The best way I can think to do this is with some code like:
SELECT Item_ID, Group_Number, Is_Active,
COUNT(*) OVER (PARTITION BY Group_Number, Is_Active ORDER BY Group_Number) [Members_In_Set],
CASE WHEN Is_Active = 1 AND COUNT(*) OVER (PARTITION BY Group_Number, Is_Active ORDER BY Group_Number) = 1 THEN 1 ELSE 0 END [Is_Last]
FROM My_Table
The Members_In_Set column is to demonstrate what the count returns when partitioning and then the CASE shows how to use this value along with Is_Active to get the result you're after
CREATE DATABASE TEST
USE TEST
CREATE TABLE TBL_TEMP
(
ID INT,
NAME VARCHAR(100),
CREATED_ON DATETIME
)
INSERT INTO TBL_TEMP VALUES (1, 'A', NULL)
INSERT INTO TBL_TEMP VALUES (2, 'B', NULL)
INSERT INTO TBL_TEMP VALUES (3, 'C', NULL)
INSERT INTO TBL_TEMP VALUES (4, 'D', NULL)
SELECT TOP 1 *
FROM TBL_TEMP
ORDER BY CREATED_ON
Result:
ID NAME CREATED_ON
------------------
2 B NULL
SELECT TOP 1 * FROM TBL_TEMP
Result:
ID NAME CREATED_ON
--------------------
1 A NULL
Why top 1 gives two different results, is it that when order by clause is used it picks random row and when not used then it gives proper top record ?
is it a kind of bug in sql server 2008 ?
SQL does not guarantee an order unless you specify an ORDER BY clause, so in the second example you get the first-inserted row by good fortune.
If you specify an ORDER BY clause, the order is not defined if the values to sort on are identical. SQL could have selected any one of the four.
This is not a bug, but defined behaviour in SQL.