SQL Query Support - sql

I have a transaction table, i want to extract those transactions from the table whose time difference is of 2 hours and transactions are performed in 2 different countries.
I have written the following query but the problem is that it is fetching those transactions as well whose countries are same.
Query is:
SELECT DISTINCT b.*
FROM TRANSACTION_TABLE b, TRANSACTION_TABLE a
WHERE
b.CARD IN (SELECT b.CARD
FROM TRANSACTION_TABLE b, TRANSACTION_TABLE a
WHERE b.TYPE_TXN IN ('21')
AND b.RESPONSE_TXN='00'
AND b.DATETIME_TXN BETWEEN DATEADD(hh,-2,GETDATE()) AND GETDATE()
AND b.CARD=a.CARD
AND b.COUNTRY<>a.COUNTRY
GROUP BY B.CARD
HAVING COUNT(B.CARD)>1 )
AND b.TYPE_TXN in ('21')
AND b.RESPONSE_TXN='00'
AND b.DATETIME_TXN BETWEEN DDATEADD(hh,-2,GETDATE()) AND GETDATE()
AND b.CARD=a.CARD
AND b.COUNTRY<>a.COUNTRY
Please guide.
Thanks

It looks that you should put something like that:
select A.*
from Transaction_Table A,
Transaction_Table B
where -- both transaction should have the same CARD
(A.Card = B.Card) and
-- ... But different countries
-- In order to prevent duplicates > instead of <>
(A.Country > B.Country) and
-- Time difference btw A and B should be less than 2 hours
-- NB! DMBS dependent! (Oracle version implemented)
((Abs(A.DateTime_Txn - B.DateTime_Txn) < 2 / 24) and
-- Other transaction filters
(A.Type_TXN in ('21')) and
(B.Type_TXN in ('21')) and
(A.Response_TXN = '00') and
(B.Response_TXN = '00')

Related

Simple sql query to copy fields from another table based on the same key

I'm looking for the simplest query that will allow me to achieve copying some value from table A to table B while they both have a same key to identify what's needed.
I basically have (for example purposes only) 2 tables that represent users.
Table A contains USER_NAME, USER_ID, JOINED_DATE, RANK
Table B contains USER_ID, ACCOUNT_DETAILS, ADDRESS, RANK
I had a little bug with the RANK one, and i now see that in some cases the RANK is updated only in B, meaning in A its always NULL, but in some case its available at B.
I want to run a DB update that will:
check what USERS were created via table A joined last 30 days,
and then take that USER_ID and use this Key on table B
and check "if this ID in table B has a RANK,
copy that RANK to the same USER_ID in table A".
To clarify - All RANK in Table A is empty which is a bug, Some RANK in Table B has data and some is NULL, this is as designed and its OK. what i want is for both RANK columns to be example the same , in some time period, not generally from the beginning of time.
If you take a look at the example image below, you can see that it copied (based on the condition that its not null and joined date is above 2019:
123 - copied RANK since its valid
111 - copied RANK since its valid
121 - wasn't copied since RANK is empty and date is below 2020
141 - wasn't coped since RANK exists but date is below 2020
I hope that's understandable, please ask if there are more questions :)
Many thanks in advance good people!
pretty new and haven't touched SQL in quite a while.
this is Oracle SQL if it matters.
You can use the simple update as follows:
Update tablea a
Set a.rank = (select b.rank
From tableb b where b.user_id = a.user_id)
Where exists (select 1 from
Tableb b where b.user_id = a.user_id
And b.rank is not null)
And a.rank is null;
--And a.joined_date >= add_months(trunc(sysdate), -1);
Just use a join:
select a.*, b.rank
from a left join
b
on a.user_id = b.user_id and
a.joined_date >= sysdate - interval '30' day
If you want to update the rank in A:
update a
set rank = (select b.rank
from b
where a.user_id = b.user_id
)
where a.joined_date >= sysdate - interval '30' day;
The date comparisons would be slightly different if you want only dates in 2020.

Recursive subtraction from two separate tables to fill in historical data

I have two datasets hosted in Snowflake with social media follower counts by day. The main table we will be using going forward (follower_counts) shows follower counts by day:
This table is live as of 4/4/2020 and will be updated daily. Unfortunately, I am unable to get historical data in this format. Instead, I have a table with historical data (follower_gains) that shows net follower gains by day for several accounts:
Ideally - I want to take the follower_count value from the minimum date in the current table (follower_counts) and subtract the sum of gains (organic + paid gains) for each day, until the minimum date of the follower_gains table, to fill in the follower_count historically. In addition, there are several accounts with data in these tables, so it would need to be grouped by account. It should look like this:
I've only gotten as far as unioning these two tables together, but don't even know where to start with looping through these rows:
WITH a AS (
SELECT
account_id,
date,
organizational_entity,
organizational_entity_type,
vanity_name,
localized_name,
localized_website,
organization_type,
total_followers_count,
null AS paid_follower_gain,
null AS organic_follower_gain,
account_name,
last_update
FROM follower_counts
UNION ALL
SELECT
account_id,
date,
organizational_entity,
organizational_entity_type,
vanity_name,
localized_name,
localized_website,
organization_type,
null AS total_followers_count,
organic_follower_gain,
paid_follower_gain,
account_name,
last_update
FROM follower_gains)
SELECT
a.account_id,
a.date,
a.organizational_entity,
a.organizational_entity_type,
a.vanity_name,
a.localized_name,
a.localized_website,
a.organization_type,
a.total_followers_count,
a.organic_follower_gain,
a.paid_follower_gain,
a.account_name,
a.last_update
FROM a
ORDER BY date desc LIMIT 100
UPDATE: Changed union to union all and added not exists to remove duplicates. Made changes per the comments.
NOTE: Please make sure you don't post images of the tables. It's difficult to recreate your scenario to write a correct query. Test this solution and update so that I can make modifications if necessary.
You don't loop through in SQL because its not a procedural language. The operation you define in the query is performed for all the rows in a table.
with cte as (SELECT a.account_id,
a.date,
a.organizational_entity,
a.organizational_entity_type,
a.vanity_name,
a.localized_name,
a.localized_website,
a.organization_type,
(a.follower_count - (b.organic_gain+b.paid_gain)) AS follower_count,
a.account_name,
a.last_update,
b.organic_gain,
b.paid_gain
FROM follower_counts a
JOIN follower_gains b ON a.account_id = b.account_id
AND b.date < (select min(date) from
follower_counts c where a.account.id = c.account_id)
)
SELECT b.account_id,
b.date,
b.organizational_entity,
b.organizational_entity_type,
b.vanity_name,
b.localized_name,
b.localized_website,
b.organization_type,
b.follower_count,
b.account_name,
b.last_update,
b.organic_gain,
b.paid_gain
FROM cte b
UNION ALL
SELECT a.account_id,
a.date,
a.organizational_entity,
a.organizational_entity_type,
a.vanity_name,
a.localized_name,
a.localized_website,
a.organization_type,
a.follower_count,
a.account_name,
a.last_update,
NULL as organic_gain,
NULL as paid_gain
FROM follower_counts a where not exists (select 1 from
follower_gains c where a.account_id = c.account_id AND a.date = c.date)
You could do something like this, instead of using the variable you can just wrap it another bracket and write at end ) AS FollowerGrowth
DECLARE #FollowerGrowth INT =
( SELECT total_followers_count
FROM follower_gains
WHERE AccountID = xx )
-
( SELECT TOP 1 follower_count
FROM follower_counts
WHERE AccountID = xx
ORDER BY date ASCENDING )

Oracle Left Join not returning all rows

I am using the following CTE. The first part collects all unique people and the second left joins the unique people with events during a particular time frame. I am expecting that all the rows be returned from my unique people table even if they don't have an event within the time frame. But this doesn't appear to be the case.
WITH DISTINCT_ATTENDING(ATTENDING) AS
(
SELECT DISTINCT ATTENDING
FROM PEOPLE
WHERE ATTENDING IS NOT NULL
), -- returns 62 records
EVENT_HISTORY(ATTENDING, TOTAL) AS
(
SELECT C.ATTENDING,
COUNT(C.ID)
FROM DISTINCT_ATTENDING D
LEFT JOIN PEOPLE C
ON C.ATTENDING = D.ATTENDING
AND TO_DATE(C.DATE, 'YYYYMMDD') < TO_DATE('20140101', 'YYYYMMDD')
GROUP BY C.ATTENDING
ORDER BY C.ATTENDING
)
SELECT * FROM EVENT_HISTORY; -- returns 49 rows
What am I doing wrong here?
Jonny
The problem is inthe column "C.ATTENDING", just change for "D.ATTENDING"
SELECT D.ATTENDING,
COUNT(C.ID)
FROM DISTINCT_ATTENDING D
LEFT JOIN PEOPLE C
ON C.ATTENDING = D.ATTENDING
AND TO_DATE(C.DATE, 'YYYYMMDD') < TO_DATE('20140101', 'YYYYMMDD')
GROUP BY D.ATTENDING
ORDER BY D.ATTENDING
Your query seems too complicated. I think the following does the same thing:
SELECT P.ATTENDING,
SUM(CASE WHEN TO_DATE(P.DATE, 'YYYYMMDD') < TO_DATE('20140101', 'YYYYMMDD')
THEN 1 ELSE 0 END)
FROM PEOPLE P
WHERE P.ATTENDING IS NOT NLL
GROUP BY P.ATTENDING
ORDER BY P.ATTENDING ;
Your problem is that you are aggregating by a column in the second table of a left join. This is NULL when there is no match.

Error while using group by

In table A I have the dates, and in B I have the order numbers.
In both tables I have a common field called order Id.
I just have a simple goal to fetch the number of orders on each date[ as in 1st, 2nd ..]
Here is what I have tried as I dont want to use joins or views.
select
A.date_of_order,
count(B.order_number)
from A, B
where A.order_id=B.order_id;
group by A.date_of_order
I am getting the following error. Probably making some trivial error. Thanks in advance
Update:
After taking into consideration Dmitri and rafa s suggestions, I get the table as:
23-FEB-14 1
23-FEB-14 1
23-FEB-14 2
23-FEB-14 2
23-FEB-14 2
07-MAR-14 2
07-MAR-14 4
07-MAR-14 1
07-MAR-14 5
02-MAR-14 1
02-MAR-14 1
As I said my requirement is very simple, just get it as
23-Feb-14 10[i.e. all the orders placed on this date]
07-Mar-14 13
02-mar-14 2
WHERE should be put before GROUP BY:
select A.date_of_order,
count(B.order_number)
from A, B
where A.order_id = B.order_id -- <- possible, but join will be better here
group by A.date_of_order
If you want a condition after GROUP BY you should use HAVING
select A.date_of_order,
count(B.order_number)
from A, B
where A.order_id = B.order_id
group by A.date_of_order
having count(B.order_number) < 3 -- having demo
The WHERE clause must to be before of the GROUP BY clause.
Use TRUNC(date) to get rid of the time so the GROUP BY will work as expected.
SELECT TRUNC(A.date_of_order), COUNT(B.order_number)
FROM A, B
WHERE A.order_id=B.order_id
GROUP BY TRUNC(A.date_of_order)
Anyway it is recommended to use the ANSI-standard SQL JOIN clause instead.
SELECT TRUNC(A.date_of_order), COUNT(B.order_number)
FROM A INNER JOIN B ON A.order_id = B.order_id
-- (WHERE conditions here)
GROUP BY TRUNC(A.date_of_order)
-- (HAVING conditions here)
The possible reasons could be that
1) Dates have timestamp, in that case following query would be helpful:
select
trunc(A.date_of_order),
count(B.order_number)
from A, B
where A.order_id=B.order_id
group by trunc(A.date_of_order);
2) Since in your sample data, you already have count against the dates, you need to take sum instead of count for your query
select
A.date_of_order,
sum(B.order_number)
from A, B
where A.order_id=B.order_id
group by A.date_of_order;
3) Or could be both, in that case you can try
select
trunc(A.date_of_order),
sum(B.order_number)
from A, B
where A.order_id=B.order_id
group by trunc(A.date_of_order);
I don't see why you need to join to that other table at all. Try just running:
select date_of_order, count(order_id) from tbl_a group by date_of_order
Your question states that table A contains rows for each order ID and date, and all you want to do is count the number of orders by date.

How to perform running sum (balance) in SQL

I have 2 SQL Tables
unit_transaction
unit_detail_transactions
(tables schema here: http://sqlfiddle.com/#!3/e3204/2 )
What I need is to perform an SQL Query in order to generate a table with balances. Right now I have this SQL Query but it's not working fine because when I have 2 transactions with the same date then the balance is not calculated correctly.
SELECT
ft.transactionid,
ft.date,
ft.reference,
ft.transactiontype,
CASE ftd.isdebit WHEN 1 THEN MAX(ftd.debitaccountid) ELSE MAX(ftd.creditaccountid) END as financialaccountname,
CAST(COUNT(0) as tinyint) as totaldetailrecords,
ftd.isdebit,
SUM(ftd.amount) as amount,
balance.amount as balance
FROM unit_transaction_details ftd
JOIN unit_transactions ft ON ft.transactionid = ftd.transactionid
JOIN
(
SELECT DISTINCT
a.transactionid,
SUM(CASE b.isdebit WHEN 1 THEN b.amount ELSE -ABS(b.amount) END) as amount
--SUM(b.debit-b.credit) as amount
FROM unit_transaction_details a
JOIN unit_transactions ft ON ft.transactionid = a.transactionid
CROSS JOIN unit_transaction_details b
JOIN unit_transactions ft2 ON ft2.transactionid = b.transactionid
WHERE (ft2.date <= ft.date)
AND ft.unitid = 1
AND ft2.unitid = 1
AND a.masterentity = 'CONDO-A'
GROUP BY a.transactionid,a.amount
) balance ON balance.transactionid = ft.transactionid
WHERE
ft.unitid = 1
AND ftd.isactive = 1
GROUP BY
ft.transactionid,
ft.date,
ft.reference,
ft.transactiontype,
ftd.isdebit,
balance.amount
ORDER BY ft.date DESC
The result of the query is this:
Any clue on how to perform a correct SQL that will show me the right balances ordered by transaction date in descendant mode?
Thanks a lot.
EDIT: THINK OF 2 POSSIBLE SOLUTIONS
The problem is generated when you have the same date in 2 transactions, so here is what Im going to do:
Save Date and Time into "date" column. That way there won't be 2 exact dates.
OR
Create a "priority" column and set the priority for each record. So if I found that the date already exists and it has priority = 1 then the current priority will be 2.
What do you think?
There are two ways to do a running sum. I am going to show the syntax on a simpler table, to give you an idea.
Some databases (Oracle, PostgreSQL, SQL Server 2012, Teradata, DB2 for instance) support cumulative sums directly. For this you use the following function:
select sum(<val>) over (partition by <column> order by <ordering column>)
from t
This is a windows function that will calculate the running sum of for each group of records identified by . The order of the sum is .
Alas, many databases don't support this functionality, so you would need to do a self join to do this in a single SELECT query in the database:
select t.column, sum(tprev.<val>) as cumsum
from t left join
t tprev
where t.<column> = tprev.<column> and
t.<ordering column> >= tprev.<ordering column>
group by t.column
There is also the possibility of creating another table and using a cursor to assign the cumulative sum, or of doing the sum at the application level.