Naming items in Value clause for readability - sql-server

Is there a better way to achieve mapping items in the values to column names. I always do this using comments:
insert into Table1 (Col1, Col2, ..., Coln)
values ('One' /* Col1 */, 'Two' /* Col2 */, ..., 'ValN' /* Coln */);
It becomes especially important when there are many columns involved and the values clause has queries in it. I always have to count and see which item in the values clause is for which column. Therefore, I just use comments as shown above. I will state the obvious that sometimes depending on the query, I may keep them on separate lines.
Any other way?

Yours is a good way. I usually use one line for each column. It can get really long but I find it more readable. Something like this:
insert into Table1 (
Col1
,Col2
,...
,Coln
)
values (
One /* Col1 */
,Two /* Col2 */
,...
,ValN /* Coln */
);
In some cases, using a JSON string could be useful. In other cases it maybe an overkill. Here's a simple example:
Let's say we have a table called "test" with two columns: "Number" (INT) and "Word" (VARCHAR(40))
We can insert records in that table using the following construction:
INSERT INTO test (Number, Word)
SELECT source.Number, source.Word
FROM
(
SELECT Number, Word
FROM
OpenJson('[{
"number": 01, "word": "This"
}, {
"number": 02, "word": "Is"
}, {
"number": 03, "word": "Just"
}, {
"number": 04, "word": "a"
}, {
"number": 05, "word": "Test"
}] '
) WITH (Number INT '$.number', Word VARCHAR(40) '$.word')
) AS source
As we can see:
The data to insert is stored inside the JSON string. We then name the each property in the JSON string using the same column names in the table.
When doing the actual insert, the names of the values to insert are variables that have the same name than their columns, making it very readable. (source.Number, source.Word).
In some cases, using the JSON string can help out not only with the mapping of the data and their columns but also debugging the data and finding wrong values.

Related

Snowflake: Trouble getting numbers to return from a PIVOT function

I am moving a query from SQL Server to Snowflake. Part of the query creates a pivot table. The pivot table part works fine (I have run it in isolation, and it pulls numbers I expect).
However, the following parts of the query rely on the pivot table- and those parts fail. Some of the fields return as a string-type. I believe that the problem is Snowflake is having issues converting string data to numeric data. I have tried CAST, TRY_TO_DOUBLE/NUMBER, but these just pull up 0.
I will put the code down below, and I appreciate any insight as to what I can do!
CREATE OR REPLACE TEMP TABLE ATTR_PIVOT_MONTHLY_RATES AS (
SELECT
Market,
Coverage_Mo,
ZEROIFNULL(TRY_TO_DOUBLE('Starting Membership')) AS Starting_Membership,
ZEROIFNULL(TRY_TO_DOUBLE('Member Adds')) AS Member_Adds,
ZEROIFNULL(TRY_TO_DOUBLE('Member Attrition')) AS Member_Attrition,
((ZEROIFNULL(CAST('Starting Membership' AS FLOAT))
+ ZEROIFNULL(CAST('Member Adds' AS FLOAT))
+ ZEROIFNULL(CAST('Member Attrition' AS FLOAT)))-ZEROIFNULL(CAST('Starting Membership' AS FLOAT)))
/ZEROIFNULL(CAST('Starting Membership' AS FLOAT)) AS "% Change"
FROM
(SELECT * FROM ATTR_PIVOT
WHERE 'Starting Membership' IS NOT NULL) PT)
I realize this is a VERY big question with a lot of moving parts... So my main question is: How can I successfully change the data type to numeric value, so that hopefully the formulas work in the second half of the query?
Thank you so much for reading through it all!
EDITED FOR SHORTENING THE QUERY WITH UNNEEDED SYNTAX
CAST(), TRY_TO_DOUBLE(), TRY_TO_NUMBER(). I have also put the fields (Starting Membership, Member Adds) in single and double quotation marks.
Unless you are quoting your field names in this post just to highlight them for some reason, the way you've written this query would indicate that you are trying to cast a string value to a number.
For example:
ZEROIFNULL(TRY_TO_DOUBLE('Starting Membership'))
This is simply trying to cast a string literal value of Starting Membership to a double. This will always be NULL. And then your ZEROIFNULL() function is turning your NULL into a 0 (zero).
Without seeing the rest of your query that defines the column names, I can't provide you with a correction, but try using field names, not quoted string values, in your query and see if that gives you what you need.
You first mistake is all your single quoted columns names are being treated as strings/text/char
example your inner select:
with ATTR_PIVOT(id, studentname) as (
select * from values
(1, 'student_a'),
(1, 'student_b'),
(1, 'student_c'),
(2, 'student_z'),
(2, 'student_a')
)
SELECT *
FROM ATTR_PIVOT
WHERE 'Starting Membership' IS NOT NULL
there is no "starting membership" column and we get all the rows..
ID
STUDENTNAME
1
student_a
1
student_b
1
student_c
2
student_z
2
student_a
So you need to change 'Starting Membership' -> "Starting Membership" etc,etc,etc
As Mike mentioned, the 0 results is because the TRY_TO_DOUBLE always fails, and thus the null is always turned to zero.
now, with real "string" values, in real named columns:
with ATTR_PIVOT(Market, Coverage_Mo, "Starting Membership", "Member Adds", "Member Attrition") as (
select * from values
(1, 10 ,'student_a', '23', '150' )
)
SELECT
Market,
Coverage_Mo,
ZEROIFNULL(TRY_TO_DOUBLE("Starting Membership")) AS Starting_Membership,
ZEROIFNULL(TRY_TO_DOUBLE("Member Adds")) AS Member_Adds,
ZEROIFNULL(TRY_TO_DOUBLE("Member Attrition")) AS Member_Attrition
FROM ATTR_PIVOT
WHERE "Starting Membership" IS NOT NULL
we get what we would expect:
MARKET
COVERAGE_MO
STARTING_MEMBERSHIP
MEMBER_ADDS
MEMBER_ATTRITION
1
10
0
23
150

SQL assignment: Using Variables and using IF statements

Okay, So here is the first question on the assignment. I just don't know where to start with the problem. If anyone could just help me get started I'd be able to figure it out probably. Thanks
Set two variable values as follows:
#minEnrollment = 10
#maxEnrollment = 20
Determine the number of courses with enrollments between the values assigned to #minEnrollment and #maxEnrollment. If there are courses with enrollments between these two values, display a message in the form
There is/are __class(es) with enrollments between __ and __..
If there are no classes within the defined range, display a message in the form
“
There are no classes with an enrollment between __ and __ students.”
.....
And here is the database to use:
CREATE TABLE Faculty
(Faculty_ID INT PRIMARY KEY IDENTITY,
LastName VARCHAR (20) NOT NULL,
FirstName VARCHAR (20) NOT NULL,
Department VARCHAR (10) SPARSE NULL,
Campus VARCHAR (10) SPARSE NULL);
INSERT INTO Faculty VALUES ('Brown', 'Joe', 'Business', 'Kent');
INSERT INTO Faculty VALUES ('Smith', 'John', 'Economics', 'Kent');
INSERT INTO Faculty VALUES ('Jones', 'Sally', 'English', 'South');
INSERT INTO Faculty VALUES ('Black', 'Bill', 'Economics', 'Kent');
INSERT INTO Faculty VALUES ('Green', 'Gene', 'Business', 'South');
CREATE TABLE Course
(Course_ID INT PRIMARY KEY IDENTITY,
Ref_Number CHAR (5) CHECK (Ref_Number LIKE '[0-9][0-9][0-9][0-9][0-9]'),
Faculty_ID INT NOT NULL REFERENCES Faculty (Faculty_ID),
Term CHAR (1) CHECK (Term LIKE '[A-C]'),
Enrollment INT NULL DEFAULT 0 CHECK (Enrollment < 40))
INSERT INTO Course VALUES ('12345', 3, 'A', 24);
INSERT INTO Course VALUES ('54321', 3, 'B', 18);
INSERT INTO Course VALUES ('13524', 1, 'B', 7);
INSERT INTO Course VALUES ('24653', 1, 'C', 29);
INSERT INTO Course VALUES ('98765', 5, 'A', 35);
INSERT INTO Course VALUES ('14862', 2, 'B', 14);
INSERT INTO Course VALUES ('96032', 1, 'C', 8);
INSERT INTO Course VALUES ('81256', 5, 'A', 5);
INSERT INTO Course VALUES ('64321', 2, 'C', 23);
INSERT INTO Course VALUES ('90908', 3, 'A', 38);
Your request is how to get started, so I'm going to focus on that instead of any specific code.
Start by getting the results that are being asked for, then move on to formatting them as requested.
First, work with the Course table and your existing variables, #minEnrollment = 10 and #maxEnrollment = 20, to get the list that meets the enrollment requirements. Hint: WHERE and BETWEEN. (The Faculty table you have listed doesn't factor into this at all.) After you're sure you have the right results in that list, use the COUNT function to get the number you need for your answer, and assign that value to a new variable.
Now, to the output. IF your COUNT variable is >0, CONCATenate a string together using your variables to fill in the values in the sentence you're supposed to write. ELSE, use the variables to fill in the other sentence.
Part of the problem is, you've actually got 3 or so questions in your post. So instead of trying to post a full answer, I'm instead going to try to get you started with each of the subquestions.
Subquestion #1 - How to assign variables.
You'll need to do some googling on 'how to declare a variable in SQL' and 'how to set a variable in SQL'. This one won't be too hard.
Subquestion #2 - How to use variables in a query
Again, you'll need to google how to do this - something like 'How to use a variable in a SQL query'. You'll find this one is pretty simple as well.
Subquestion #3 - How to use IF in SQL Server.
Not to beat a dead horse, but you'll need to google this. However, one thing I would like to note: I'd test this one first. Ultimately, you're going to want something that looks like this:
IF 1 = 1 -- note, this is NOT the correct syntax (on purpose.)
STUFF
ELSE
OTHERSTUFF
And then switch it to:
IF 1 = 2 -- note, this is NOT the correct syntax (on purpose.)
STUFF
ELSE
OTHERSTUFF
... to verify the 'STUFF' happens when the case is true, and that it otherwise does the 'OTHERSTUFF'. Only after you've gotten it down, should you try to integrate it in with your query (otherwise, you'll get frustrated not knowing what's going on, and it'll be tougher to test.)
One step at a time. Let me give you some help:
Set two variable values as follows: #minEnrollment = 10 #maxEnrollment
= 20
Translated to SQL, this would look like:
Declare #minEnrollment integer = 10
Declare #maxEnrollment integer =15
Declare #CourseCount integer = 0
Determine the number of courses with enrollments between the values
assigned to #minEnrollment and #maxEnrollment.
Now you have to query your tables to determine the count:
SET #CourseCount = (SELECT Count(Course_ID) from Courses where Enrollment > #minEnrollment
This doesn't answer your questions exactly (ON PURPOSE). Hopefully you can spot the mistakes and fix them yourself. The other answers gave you some helpful hints as well.

How to change index?

I want to column index change.
For example:
Column name: MemberID
Sample data:
MemberID1: M100045
MemberID2: M100046
MemberID3: M100047
Expected results:
Member ID1: T200045
Member ID2: T200046
Member ID3: T200047
How I do change 5 index SQL query?
The index will be updated when the data is updated. Thus, you just need to update the data.
update YourTable
set yourColumn = stuff(yourColumn,1,2,'T2')
STUFF() inserts a string into another string, and optionally deletes a specified length of characters. In the code above, we are inserting T2 starting at position 1, and deleting 2 character (M1 in this case).

Column does not exits in Postgresql

I created a table:
CREATE TABLE LuxRoom
(
roomID INT NOT NULL PRIMARY KEY CONSTRAINT roomID_ref_room REFERENCES Room(roomID),
peopleNumber INT NOT NULL,
additionalService TEXT
)
But when I do insert like this:
INSERT INTO LuxRoom(roomID, peopleNumber, additionalService) VALUES(1, 2, "Extra food, a cab");
I get this error:
ERROR: column "Extra food, a cab" does not exist
What is the problem?
Single quotes delimit a string constant or a date/time constant.
Double quotes delimit identifiers for e.g. table names or column
names. This is generally only necessary when your identifier doesn't
fit the rules for simple identifiers.
replace double quotes with single quote
INSERT INTO LuxRoom (roomID, peopleNumber, additionalService)
VALUES (1, 2, 'Extra food, a cab');
PostgreSQL is "picky" in terms of SQL syntax and makes a difference between ' and ". " indicates, that it's an object, ' it's a string. So your query should look like
INSERT INTO LuxRoom(roomID, peopleNumber, additionalService)
VALUES(1, 2, 'Extra food, a cab');
This is different to e.g. behaviour on MySQL, but the standard way.

Sum of values of json array in PostgreSQL

In PostgreSQL 9.3, I have a table like this
id | array_json
---+----------------------------
1 | ["{123: 456}", "{789: 987}", "{111: 222}"]
2 | ["{4322: 54662}", "{123: 5121}", "{1: 5345}" ... ]
3 | ["{3232: 413}", "{5235: 22}", "{2: 5453}" ... ]
4 | ["{22: 44}", "{12: 4324}", "{234: 4235}" ... ]
...
I want to get the sum of all values in array_json column. So, for example, for first row, I want:
id | total
---+-------
1 | 1665
Where 1665 = 456 + 987 + 222 (the values of all the elements of json array). No previous information about the keys of the json elements (just random numbers)
I'm reading the documentation page about JSON functions in PostgreSQL 9.3, and I think I should use json_each, but can't find the right query. Could you please help me with it?
Many thanks in advance
You started looking at the right place (going to the docs is always the right place).
Since your values are JSON arrays -> I would suggest using json_array_elements(json)
And since it's a json array which you have to explode to several rows, and then combine back by running sum over json_each_text(json) - it would be best to create your own function (Postgres allows it)
As for your specific case, assuming the structure you provided is correct, some string parsing + JSON heavy wizardry can be used (let's say your table name is "json_test_table" and the columns are "id" and "json_array"), here is the query that does your "thing"
select id, sum(val) from
(select id,
substring(
json_each_text(
replace(
replace(
replace(
replace(
replace(json_array,':','":"')
,'{',''),
'}','')
,']','}')
,'[','{')::json)::varchar
from '\"(.*)\"')::int as val
from json_test_table) j group by id ;
if you plan to run it on a huge dataset - keep in mind string manipulations are expensive in terms of performance
You can get it using this:
/*
Sorry, sqlfiddle is busy :p
CREATE TABLE my_table
(
id bigserial NOT NULL,
array_json json[]
--,CONSTRAINT my_table_pkey PRIMARY KEY (id)
)
INSERT INTO my_table(array_json)
values (array['{"123": 456}'::json, '{"789": 987}'::json, '{"111": 222}'::json]);
*/
select id, sum(json_value::integer)
from
(
select id, json_data->>json_object_keys(json_data) as json_value from
(
select id, unnest(array_json) as json_data from my_table
) A
) B
group by id

Resources