How can I avoid "stringly typed" code in T-SQL? - sql

Consider some code like
SELECT
CASE
WHEN [DECISION-MAKER] = 'COKE' THEN 'GIVE COKE'
WHEN [DECISION-MAKER] IN ('PEPSI', 'BLOOD') THEN 'GIVE DEATH'
END AS [EMPLOYEE-ASSIGNMENT],
CASE [DECISION-MAKER]
WHEN 'COKE' THEN 'Employee prefers coke. Give coke.'
WHEN 'PEPSI' THEN 'Employee prefers pepsi. Give death.'
WHEN 'BLOOD' THEN 'Employee is some sort of vampire. Give death.'
END AS [ASSIGNMENT-REASON]
FROM
(
SELECT *,
CASE
WHEN [COMPLEX-LOGIC-1] THEN 'COKE'
WHEN [COMPLEX-LOGIC-2] THEN 'PEPSI'
WHEN [COMPLEX-LOGIC-3] THEN 'BLOOD'
END AS [DECISION-MAKER]
FROM [WHEREVER]
)
I believe that the technical term for such poor code is "stringly typed". The key issue in the above code is that decisions are being made based on a string output that the developer needs to type and consistently get correct. If anything goes wrong, the language will be incapable of throwing errors. In a traditional language, the workaround would be to construct some sort of dictionary to handle these cases. What is the idiomatic solution in T-SQL? I don't like the idea of making a use-once table, but maybe a temp table would be helpful?

I wouldn't worry about having a "Use in one place" table. If you still worry about it cluttering up your database, you can use a table variable.
DECLARE #AssignmentAndReason AS TABLE (
DECISION-MAKER varchar,
EMPLOYEE-ASSIGNMENT varchar,
ASSIGNMENT-REASON varchar
);
INSERT INTO #AssignmentAndReason VALUES
('COKE', 'GIVE COKE', 'Employee prefers coke. Give coke.'),
('PEPSI', 'GIVE DEATH', 'Employee prefers pepsi. Give death.'),
('BLOOD', 'GIVE DEATH', 'Employee is some sort of vampire. Give death.');
SELECT [EMPLOYEE-ASSIGNMENT], [ASSIGNMENT-REASON],
FROM [WHEREVER]
JOIN #AssignmentAndReason ON [DECISION-MAKER] = CASE
WHEN [COMPLEX-LOGIC-1] THEN 'COKE'
WHEN [COMPLEX-LOGIC-2] THEN 'PEPSI'
WHEN [COMPLEX-LOGIC-3] THEN 'BLOOD'
END

uses "with as(query)"
with firstLogic as
(
select PersonID, Name,
CASE
WHEN valueDRINK=1 or valueDRINK=2 THEN 'COKE'
WHEN valueDRINK=3 or valueDRINK=4 THEN 'PEPSI'
WHEN valueDRINK=5 THEN 'BLOOD'
END AS [DECISION-MAKER]
from Persons
where country='co'
)
select PersonID, Name ,[DECISION-MAKER],
CASE
WHEN [DECISION-MAKER] = 'COKE' THEN 'GIVE COKE'
WHEN [DECISION-MAKER] IN ('PEPSI', 'BLOOD') THEN 'GIVE DEATH'
END AS [EMPLOYEE-ASSIGNMENT],
CASE [DECISION-MAKER]
WHEN 'COKE' THEN 'Employee prefers coke. Give coke.'
WHEN 'PEPSI' THEN 'Employee prefers pepsi. Give death.'
WHEN 'BLOOD' THEN 'Employee is some sort of vampire. Give death.'
END AS [ASSIGNMENT-REASON]
from firstLogic
"with" allows you to have different queries and then join them without creating a temporary table
you can see it in this example

Related

SQL: Consolidating results

I am a relative beginner in SQL (Learned and forgotten many times) and entirely self taught so please excuse my likely lack of proper terminology. I made a query that pulls items that have been returned, each items' row has a return code. Here is a result sample:
In the final report(Created with Visual Studio), I would like to be able to have a count of returns by return type but I would need to consolidate the 40 or so return codes into 4 or 5 return type groups. So RET_CODE values ) and . are both product quality issues and would count in the "Product Quality" row.
I could use some help with what the best way to accomplish this would be.
Thank You
Andrew
The bad way!
You could do this by creating the grouping within your SQL statement with something like
SELECT
*,
CASE
WHEN RET_CODE IN ('.', ')') THEN 'Quality Error'
WHEN RET_CODE IN ('X', 'Y', 'Z') THEN 'Some Other error'
ELSE [Desc1]
END AS GroupDescription
FROM myTable
The problem with this approach is that you have to keep repeating it every time you want to something similar.
The better option. (but not perfect!)
Assuming you do not already have such a table...
Create a table that contains the grouping. You can use this in the future whenever you need to do this kind of thing.
For example.
CREATE TABLE dbo.MyErrorGroupTable (RET_CODE varchar(10), GroupDescription varchar(50))
INSERT INTO dbo.MyErrorGroupTable VALUES
('.', 'Quality Error'),
(')', 'Quality Error'),
('X', 'Some Other Error'),
('Y', 'Some Other Error'),
('.', 'Some Other Error'),
('P', 'UPS Error'),
('A', 'PAck/Pick Error')
etc....
Then you can simply join to this table and use the GroupDescription in your report
e.g.
SELECT
a.*, b.GroupDescription
FROM myTable a
JOIN MyErrorGroupTable b ON a.RET_CODE = b.RET_CODE
Hope this helps...
You're looking for the GROUP BY clause.

SQL Subquery to replace all values

I have a query which returns a bunch of different data, however I want to have it replace all the values upon a certain condition.
What I have written below kind of gives me the result I want but not really. It creates a new column instead of replacing the other one:
SELECT
CASE
WHEN T4.[U_DestType] = '6'
THEN (SELECT
'Company Limited' AS [ShipToCode]
)
END AS [ShipToCode],
T2.[ShipToCode],
T6.[StreetS],
T6.[StreetNoS],
T6.[CityS],
T6.[ZipCodeS],
T6.[CountryS],
T5.[LicTradNum],
T2.[CardCode],
T4.[Phone1],
T4.[E_Mail],
T4.[U_DestType],
CASE
WHEN T4.[Country] = 'GB'
THEN 'EN'
ELSE T4.[Country]
END AS [Country],
T4.[U_ShortName]
FROM[...]
The end goal is to replace all of the columns with some preset values instead of just ShipToCode as above.
I tried putting an EXIST subquery after FROM too but that didn't work either.
Is this possible? I'm probably missing something very obvious.
Many thanks!
You can use an ELSE in your CASE expression to combine the two "columns":
CASE
WHEN T4.[U_DestType] = '6'
THEN (SELECT
'Company Limited' AS [ShipToCode]
)
ELSE T2.[ShipToCode]
END AS [ShipToCode],
And by the way, you didn't need to use a Sub-Select. This would work just as well and is easier to read:
CASE
WHEN T4.[U_DestType] = '6' THEN 'Company Limited'
ELSE T2.[ShipToCode]
END AS [ShipToCode],

Decode function in oracle for null values

I am working on a java code in which i am comparing the user input values with database values.
I am writing the code to display the alert message to the users on selection of particular division from drop down menu. But there are some alert message which I want to display to all users of every division.
I have the table columns as sr_n0, alert_desc, div_code, alert_flag.
Initially I am checking the div_code = 'division code' and alert_flag = 'y' to display division specific alert message.
But now I want to know how the decode function will work if div_code = null and alert_flag = 'y'.
I have tried this SQL query :
SELECT DECODE(DIV_CODE,'61','Division A',
'62','Division B',
'ALL')
FROM ALERTS WHERE ALERT_FLAG='Y';
If there is no match, then the result will be 'ALL'. I would strongly encourage you to use the ANSI-standard CASE statement, instead of the Oracle-specific DECODE() function:
SELECT (CASE DIV_CODE
WHEN '61' THEN 'Division A'
WHEN '62' THEN 'Division B'
ELSE 'ALL'
END)
FROM ALERTS
WHERE ALERT_FLAG = 'Y';
I solved this issue by writing simple SQL command and it worked for me.
SELECT ALERT_DESC FROM ALERTS A
WHERE
A.DIV_CODE IS NULL
AND A.ALERT_FLAG='Y' ;

How to get Column Data types of particular table in SSAS using MDX

I have a query where I'm getting all tables and column names, but how to get Data Types and length values of columns of particular table?
SELECT [CATALOG_NAME] as [DATABASE],
CUBE_NAME AS [CUBE],[DIMENSION_UNIQUE_NAME] AS [DIMENSION],
LEVEL_CAPTION AS [ATTRIBUTE],
[LEVEL_NAME_SQL_COLUMN_NAME] AS [ATTRIBUTE_NAME_SQL_COLUMN_NAME],
[LEVEL_KEY_SQL_COLUMN_NAME] AS [ATTRIBUTE_KEY_SQL_COLUMN_NAME]
FROM $system.MDSchema_levels
WHERE CUBE_NAME ='Adventure Works'
AND level_origin=2
AND LEVEL_NAME <> '(All)'
order by [DIMENSION_UNIQUE_NAME]
Your friend is $SYSTEM.MDSCHEMA_PROPERTIES.
The query would probably be somewhat like :
select [HIERARCHY_UNIQUE_NAME], [LEVEL_UNIQUE_NAME], DATA_TYPE
from $SYSTEM.MDSCHEMA_PROPERTIES
where [Dimension_Unique_Name] = '[Accident Cause]'
and [Property_Name] = 'MEMBER_VALUE'
and [CUBE_NAME] = 'Adventure Works'
To understand what the values in column DATA_TYPE mean, refer this msdn link.

SQL Server 2005 Issue Column name or number of supplied values does not match table definition

I wish to DELETE the data from a table before performing an INSERT INTO, however I keep recieving an error stating:
Insert Error: Column name or number of supplied values does not match table definition.
I've also tried defining the columns the data should be entered into as part of the INSERT INTO statement, but then get issues with column names, even though they are correct. I have a feeling the issues relates to me selecting 2 PostCode entries and converting them into 1, but if someone could shed light on this it would be a big help.
My code can be found below, if you want me to add the code where I was sepcifing column names let me know. So you know the fields selected are all the fields in the Course table other than AutoNum which is a auto number primary key and SSMA_TimeStamp, which is a TimeStamp.
BEGIN
DELETE dbo.Course
INSERT INTO dbo.Course
SELECT
RTRIM( CAST (sd.[RefNo] AS nvarchar(50))) AS 'Student Ref No',
sd.[FirstForeName] AS Forename,
sd.[Surname],
sd.[Address1],
sd.[Address2],
sd.[Address3],
sd.[Address4],
sd.[DateOfBirth] AS DOB,
sd.[PostCodeOut] + ' ' + sd.[PostCodeIn] AS 'Post Code',
o.[Name] AS 'Course Name',
o.[Code] As 'Course Code',
e.[StartDate] AS 'Start Date',
e.[ExpectedGLH] AS 'Exp GLH',
e.[ExpectedEndDate] AS 'Expected End Date',
e.[ActualEndDate] AS 'Actual End Date',
e.[Grade] AS 'Grade',
ou.[Description] AS Outcome,
cs.[Description] AS 'Completion Status',
sd.[Tel1] AS 'Tel 1'
FROM [xxxxxxx].[xxxxxx].[dbo].[StudentDetail] sd
INNER JOIN [xxxxxxx].[xxxxxx].[dbo].[Enrolment] e
ON sd.[StudentDetailID] = e.[StudentDetailID]
Inner JOIN [xxxxxxx].[xxxxxx].[dbo].[Offering] o
ON o.[OfferingID] = e.[OfferingID]
INNER JOIN [xxxxxxx].[xxxxxx].[dbo].[CompletionStatus] cs
ON cs.[CompletionStatusID] = e.[CompletionStatusID]
INNER JOIN [xxxxxxx].[xxxxxx].[dbo].[Outcome] ou
ON ou.[OutcomeID] = e.[OutcomeID]
WHERE sd.[AcademicYearID] = '09/10'
AND
o.[Code] LIKE '%-ee%'
AND
o.[Name] LIKE '%-%dl%'
ORDER BY
sd.[RefNo]
It sounds like your 'Course' table does not match your insert statement, either in the number or names of the columns specified (as per the error message).
Could you add the create table code for the 'Course' table as that will show where the discrepancy lies.
Thanks.
I would explicitly list the columns in the course table that you are inserting into - this may solve your problem/help find your issue, but also reduce maintenance problems in the future.
To fix this issue you need explicitly specify list of the table's columns in the INSERT INTO statement.
you should add a list of columns to the INSERT statement, see below, where you explicitly list each column from dbo.Course that you intend to populate in your INSERT:
INSERT INTO dbo.Course
---<<<<<
(col1, col2, col3, col4, clo5....) ---<<<<<Add this here
---<<<<<
SELECT
RTRIM( CAST (sd.[RefNo] AS nvarchar(50))) AS 'Student Ref No',
sd.[FirstForeName] AS Forename,
sd.[Surname],
sd.[Address1],
sd.[Address2],
sd.[Address3],
sd.[Address4],
sd.[DateOfBirth] AS DOB,
sd.[PostCodeOut] + ' ' + sd.[PostCodeIn] AS 'Post Code',
o.[Name] AS 'Course Name',
o.[Code] As 'Course Code',
e.[StartDate] AS 'Start Date',
e.[ExpectedGLH] AS 'Exp GLH',
e.[ExpectedEndDate] AS 'Expected End Date',
e.[ActualEndDate] AS 'Actual End Date',
e.[Grade] AS 'Grade',
ou.[Description] AS Outcome,
cs.[Description] AS 'Completion Status',
sd.[Tel1] AS 'Tel 1'
FROM ....
then make sure that each column in the SELECT list matches each of these columns and in order. From your error, it sounds like you have too many any or too few returned columns in the SELECT.