SQL SSMS IF THEN with multiple criteria across same field - sql

I need to pull a transaction record from a table if it is type 'C' and has a record post time greater than or equal to the post time for a record with type 'W' where the account numbers and post date are the same. I am struggling with creating an if/then where the posttime for type 'C' >= posttime for type 'W'... any help would be appreciated. I've done these types before but never for the same field where only one record item is different.

This would be the typical method using exists:
select * from transactions t
where t.actioncode = 'C' and exists (
select 1 from transactions t2
where t2.account_num = t.account_num and t2.postdate = t1.postdate
and t2.actioncode = 'W'
and t2.posttime < t1.posttime
)

If I understand you correctly, what you describe can be accomplished through JOINS.
Think relational data sets and SARGs.
While you still have not given us a table structure (which helps enormously), the solution can help steer you in the right direction. The following assumes a FACT table of TRANSACTIONS, where the carnality to itself is M:M
SELECT TOP 1000 A.ACTIONCODE, A.TRAN_RECORD --, any other needed columns
FROM TRANSACTIONS A
INNER JOIN (SELECT ACTIONCODE, POSTTIME, ACCOUNT_NUM, POSTDATE
FROM TRANSACTIONS
WHERE ACTIONCODE = 'W') B ON A.ACCOUNT_NUM = B.ACCOUNT_NUM
AND A.POSTDATE = B.POSTDATE
WHERE A.ACTIONCODE = 'C'
AND A.POSTTIME >= B.POSTTIME
UPDATED: I accidently forgot to include the correct number of columns. Always specify the same columns (or * if you do not care) that you will be using in your INNER JOIN.
Regardless, we optimize the query by only returning results that we will be using or seeing in our query.

This is what I had originally, but it just churned in SSMS without results. Essentially, I just need all 'C' type records returned where there is a 'W' type record with a posttime less than the 'C', but where the account numbers and postdate for the record are the same. posttime, postdate, type, and number are all fields in my table.
SELECT *
FROM TRANSACTIONS
WHERE ACTIONCODE = 'C' AND POSTTIME >= POSTTIME AND ACTIONCODE = 'W'

Related

Validate my interpretation of an SQL query

my question is definitely going to be a little different, so I hope I'm still adhering to the stack overflow question etiquette. With that in mind, I'll get straight to the point.
Essentially, since I am still learning SQL I was looking at examples of scheduled queries in GCP and came across something and I wanted to see if I understand what's going on. So I took the query and wrote some comments explaining what I think the lines in the query are doing. The context in the code itself is irrelevant, I'm more curious if I'm correctly understanding what each of the clauses is doing.
Would anyone be able to tell me if I am interpreting it correctly or if I misunderstood some stuff, based on my comments? The code and comments are below. Note that the comments come first and the queries I'm commenting on follow directly after.
-- Create temporary table with the subquery below via the WITH () clause
-- Table contains session date, which webpage, total sessions, total sessions with a logout, and total clicks
-- The data in this temporary table is coming from the `gcp-project-223467.web.top_level` table in BigQuery
-- The columns correspond to dates 01/01/2022 & onwards, and exclude the 'Home'and 'Team' pages
-- The resulting data in the temp table is grouped by date & page type (first and second columns of the resulting temp table)
WITH logins AS (
SELECT
session_date as date,
website_page as page,
SUM(sessions) AS sessions,
SUM(sessions_with_logout) AS logouts,
SUM(clicks) AS clicks
FROM `gcp-project-223467.web.top_level`
WHERE DATE_session >= "2022-01-01"
AND website_page NOT IN ('Home','Team')
AND clicks > 0
GROUP BY 1, 2
)
-- Select the data from the above subquery (via SELECT logins.*)
-- Left join another temp table with data coming from `ingka-web-analytics-prod.web_data.transactions` in BigQuery
-- Left join is being done according to the logins & login_days date_hit AND logins & login_days ´logins_web´ columns.
-- The specific data taken from the aforementioned BQ table is aggregated and filtered via CASE WHEN - THEN statements
-- Further conditions are specified via the WHERE statements
-- The resulting temporary table in the subquery under LEFT JOIN is named login_days.
-- The columns in the select statement before the left join (web logins, mobile logins etc)
-- are from the temporary table in the select statement under the left join statement
SELECT
logins.*,
logins_web,
mobile_logins,
logins_ios,
logins_android,
logins_final
FROM logins
LEFT JOIN (
SELECT
date_hit as date,
website_page as page,
SUM(CASE WHEN login_type = 'web' THEN SAFE_CAST(count_logins_final AS INT64) END ) AS logins_web,
COUNT(DISTINCT CASE WHEN login_type = 'mobile' THEN login_id END ) AS mobile_logins,
SUM(CASE WHEN login_type = 'ipad' THEN SAFE_CAST(count_logins_final AS INT64) END ) AS logins_ios,
COUNT(DISTINCT CASE WHEN login_type = 'android' THEN login_id END ) AS logins_android,
COUNT(DISTINCT login_id) AS logins_final,
FROM `gcp-project-223467.web.login_data`
WHERE date_hit >= "2022-01-01" AND website_page NOT IN ('Home','Team')
AND count_logins_final != 'NaN'
AND count_logins_final NOT LIKE '%,%'
AND count_logins_final > '0'
AND website_platform != 'ibes'
AND login_type = 'Successful'
GROUP BY 1, 2
)login_days
ON logins.date = login_days.date AND logins.page = login_days.page
WHERE sessions_with_logout > 0

How to make a query where every column is a parallel count of a subquery?

I need to render a query such that every column contains the count of a respective table.
The code I have now is:
SELECT COUNT(table1.Id),
COUNT(table2.Id),
COUNT(table3.Id)
FROM table1,
table2,
table3
WHERE table1.done = 'No' OR
table2.done = 'No' OR
table3.done = 'No' OR
But I need the query to return the same result values as if every table would be counted independently, like:
SELECT COUNT(tableX.Id) FROM tableX WHERE talbeX.done = 'No'
where the 'X' stands for 1,2 or 3.
How can this be achived with SQL?
Thanks beforhand for the help.
Just use a nested sub query, exactly as you have explained it:
SELECT
(SELECT COUNT(table1.Id) FROM table1 WHERE table1.done = 'No') as T1Count,
(SELECT COUNT(table2.Id) FROM table2 WHERE table2.done = 'No') as T2Count,
(SELECT COUNT(table3.Id) FROM table3 WHERE table3.done = 'No') as T3Count,
(SELECT COUNT(tableN.Id) FROM tableN) as TNCount;
This will query the tables independently so you are free to use what ever additional criteria you may need without trying to correlate the results from each query
FROM in this case is not strictly necessary in the outer query as we are not returning rows from any specific table, there is no table that we could specify in the from clause. Each RDBMS has their own convention for these types of queries, MS SQL Server and Oracle are to predominant database engines used in Outsystems
If we did specify a table in FROM then this would return 1 row for every record in that table, which is inefficient and not required. So it is important that we do not include a FROM clause.
Transact-SQL - FROM
The FROM clause is usually required on the SELECT statement. The exception is when no table columns are listed, and the only items listed are literals or variables or arithmetic expressions.
ORACLE - DUAL Table
DUAL is a table automatically created by Oracle Database along with the data dictionary. DUAL is in the schema of the user SYS but is accessible by the name DUAL to all users. It has one column, DUMMY, defined to be VARCHAR2(1), and contains one row with a value X. Selecting from the DUAL table is useful for computing a constant expression with the SELECT statement. Because DUAL has only one row, the constant is returned only once. Alternatively, you can select a constant, pseudocolumn, or expression from any table, but the value will be returned as many times as there are rows in the table.
Update - OP is using Oracle!
After attempting the solution, OP responded that it raised the following error:
Error in advanced query SQL2: ORA-00923: FROM keyword not found where expected
The ORA prefix of this error number indicates that the data store is actually an Oracle implementation, so we need to append the FROM DUAL to the query.
SELECT
(SELECT COUNT(table1.Id) FROM table1 WHERE table1.done = 'No') as T1Count,
(SELECT COUNT(table2.Id) FROM table2 WHERE table2.done = 'No') as T2Count,
(SELECT COUNT(table3.Id) FROM table3 WHERE table3.done = 'No') as T3Count,
(SELECT COUNT(tableN.Id) FROM tableN) as TNCount
FROM DUAL;

Want to concatenate column of the second query to the first query but getting errors such as "query block has incorrect number of result columns"

SELECT
ID, PRIM_EMAIL, SEC_EMAIL, PHONE
FROM
STUDENTS.RECORDS
WHERE
ID IN (SELECT ID FROM STUDENTS.INFO WHERE ROLL_NO = '554')
UNION
SELECT NAME
FROM STUDENTS.INFO
WHERE ROLL_NO = '554';
Here Roll_No is a user inserted data so for now I have hard coded it. Basically with the help of ROLL_NO I sort the STUDENTS_INFO table from where I get the ID and based on that I try to get PRIM_EMAIL, SEC_EMAIL, PHONE from the STUDENTS.RECORDS table while matching the foreign keys of both the tables. In addition to the current result set I also want to have the prov_name column.
Any help is very much appreciated. Thank you!
I suspect that you want to put all this information on the same row, which suggests a join rather than union all:
select
r.ID,
r.PRIM_EMAIL,
r.SEC_EMAIL,
r.PHONE,
r.NAME
from STUDENTS.RECORDS r
inner join STUDENTS.INFO i ON i.ID = r.ID
where I.ROLL_NO = '554';
I think the source of your error query block has incorrect number of result columns is coming from trying to union together a table with 4 columns (id, prim_email, sec_email, phone) with 1 column (name).
From your question, I am gathering that you want a single table of id, prim_email, sec_email, phone from students.records and name from students.info.
I think the following query using CTE's might get you (partially) to your final result. You may want to refactor for optimizing performance.
with s_records as ( select * from students.records ),
s_info as ( select * from students.info ),
joined as (
select
s_records.id,
s_records.prim_email,
s_records.sec_email,
s_records.phone,
s_info.name
from s_records
left join s_info
on s_records.roll_no = s_info.roll_no
where roll_np = '554' )
select * from joined
Overall, I think that a join will be part of your solution rather than a union :-)

How to view different data depending on the value for SQL

How can I make the values show up differently?
Example:
I have a table 'Feelings' with a column called 'Happy'
If I select * from 'Happy' it will bring back values 1, 2, 3 (with user IDs to show which user is which feeling)
1 stands for Yes, 2 stands for no, 3 stands for maybe
I want the table to not show 1,2,3 but instead show yes,no,maybe
How would I go about to making this?
CASE HAPPY
WHEN '1' THEN 'Yes'
WHEN '2' THEN 'No'
WHEN '3' THEN 'Maybe'
ELSE 'Other'
END AS 'Happy'
While you can use a CASE as other answers here you can also use a join -- to a table of lookup values or to a CTE, or VALUES clause. Even a temp table in a stored procedure.
That would look like this:
SELECT user_id, COALESCE(lookup.value,'Unknown') as happy
FROM feelings
LEFT JOIN (
VALUES
('1','Yes'),
('2','No'),
('3','Maybe')
) AS lookup(k,value) ON feelings.happy = lookup.k
Using a join and storing related information in a table is a much more SQL way of doing things and offers many benefits including easier maintenance and often faster execution.
If you just have a table called Feelings and a column called Happy with just IDs, you can try the following query:
SELECT
user_id,
CASE HAPPY
WHEN '1' THEN 'Yes'
WHEN '2' THEN 'No'
WHEN '3' THEN 'Maybe'
ELSE 'Other'
END AS 'feeling_happy'
FROM FEELINGS
Otherwise, if you have 2 tables, one called Feelings with column Happy that has the IDs and another table called Happy with the IDs in one column and the defined statuses in another column (which we'll call happy_x), then you can simple use the following query:
SELECT
f.user_id, h.happy_x
FROM Feelings f
INNER JOIN Happy AS h
ON h.id = f.happy

Left Join Not Joining with a Single Record

I have the following query:
Insert into cet_database.dbo.termData
(
termID,
studentID,
course,
[current],
program,
StbyCurrentClassID,
class,
classCode,
cancelled
)
Select
fm_stg.classByStudent_termData_assessmentData.termID,
fm_stg.classByStudent_termData_assessmentData.studentID,
fm_stg.classByStudent_termData_assessmentData.class_code,
case when fm_stg.classByStudent_termData_assessmentData.[current] = 'Yes' then 1 else 0 end,
fm_stg.classByStudent_termData_assessmentData.program,
fm_stg.classByStudent_termData_assessmentData.classByStudentID,
fm_stg.classByStudent_termData_assessmentData.class,
fm_stg.classByStudent_termData_assessmentData.classID,
case when fm_stg.classByStudent_termData_assessmentData.cancelled_flag = 1 then 1 else 0 end
From fm_stg.classByStudent_termData_assessmentData left outer join termData
On fm_stg.classByStudent_termData_assessmentData.class_code = termData.course
and fm_stg.classByStudent_termData_assessmentData.termID = termData.termID
and fm_stg.classByStudent_termData_assessmentData.studentID = fm_stg.classByStudent_termData_assessmentData.studentID
Where termData.StbyCurrentClassID is null
I use the query to import data into a staging table from another database (fm_stg.classByStudent_termData_assessmentData) before importing it into my database's tables. This particular query is part of a larger stored procedure that imports data into multiple tables related to termData.
When I run the sproc, I get the record inserted into fm_stg.classByStudent_termData_assessmentData but not into termData. I am only inserting one record when having this problem, but it works for the 10,000 records I did previously. I use the left join to establish what already exists in my database's table and what doesn't, then take the relevant records from the staging table. However, with this record:
316a, 39520, DEC 10, Yes, DEC10, 105713, DEC 10 (18), 6078, NULL, 2
The select returns nothing - why is this? The record definitely doesn't exist in my termData table and records insert into all my other tables from the staging table. The sproc is running all of the inserts in a transaction so as to avoid precisely this scenario where records are inserted in some tables and not others, but it doesn't seem to be working.
You say the query worked for the previous 10,000 records, but doesn't for the current one. The only thing that looks strange in your query is the third line in your ON clause where you compare a field (the studentID) with itself.
On fm_stg.classByStudent_termData_assessmentData.class_code = termData.course
and fm_stg.classByStudent_termData_assessmentData.termID = termData.termID
and fm_stg.classByStudent_termData_assessmentData.studentID = fm_stg.classByStudent_termData_assessmentData.studentID
I am just guessing here, but as this line is in the ON clause, did you want to compare the student ID, too? So it may be you were just lucky the query worked so far and now you stumble upon the student ID. I suppose the ON clause should look like this:
On fm_stg.classByStudent_termData_assessmentData.class_code = termData.course
and fm_stg.classByStudent_termData_assessmentData.termID = termData.termID
and fm_stg.classByStudent_termData_assessmentData.studentID = termData.studentID
By the way, queries get more readable by using table aliases. In the following query I use ad for fm_stg.classByStudent_termData_assessmentData and td for termData:
Insert into cet_database.dbo.termData
(
termID,
studentID,
course,
[current],
program,
StbyCurrentClassID,
class,
classCode,
cancelled
)
Select
ad.termID,
ad.studentID,
ad.class_code,
case when ad.[current] = 'Yes' then 1 else 0 end,
ad.program,
ad.classByStudentID,
ad.class,
ad.classID,
case when ad.cancelled_flag = 1 then 1 else 0 end
From fm_stg.classByStudent_termData_assessmentData ad
Left Outer Join termData td On ad.class_code = td.course
And ad.termID = td.termID
And ad.studentID = td.studentID
Where td.StbyCurrentClassID is null;
Moreover when checking for existence, why do you use the anti-join trick? Did you have issues with a straight-forward NOT EXISTS? Use tricks only when really needed. The query reads better as follows:
Insert into cet_database.dbo.termData
(
termID,
studentID,
course,
[current],
program,
StbyCurrentClassID,
class,
classCode,
cancelled
)
Select
termID,
studentID,
class_code,
case when [current] = 'Yes' then 1 else 0 end,
program,
classByStudentID,
class,
classID,
case when cancelled_flag = 1 then 1 else 0 end
From fm_stg.classByStudent_termData_assessmentData ad
Where Not Exists
(
Select *
From termData td
Where ad.class_code = td.course
And ad.termID = td.termID
And ad.studentID = td.studentID
);
With another DBMS you could even have used NOT IN (i.e. Where (class_code, termId, studenId) Not In (Select ...)) which is not correlated so such a typo as yours could not even have occurred, but SQL Server doesn't feature tuples in the IN clause unfortunately.