How to write Joins in loop in SQL server? [closed] - tsql

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
I have a below kind of data in my table and i need to get the below kind of output.
U.Id Current_Id Previous_Id Date reason
01 aa null 21 xyz
01 bb aa 24 yxz
01 cc bb 24 out
01 dd cc 25 tot
01 aaa null 11 yyz
01 bbb aaa 12 zyy
First four records are one set and next two records are one set. we can identify this by current_id and Previous_ID columns. I need below kind of Output.
Output :
O1 - aa - 21 - 25 - tot
01 - aaa - 11 - 12 -zyy
For each set i need first and last record dates. How can i achieve this in ms sql?

You can use a recursive Common Table Expression (rCTE) to recurse through the data, and then get the respective MIN and MAX:
WITH YourTable AS(
SELECT *
FROM (VALUES('01','aa',NULL,21),
('01','bb','aa',24),
('01','cc','bb',24),
('01','dd','cc',25),
('01','aaa',NULL,11),
('01','bbb','aaa',12))V([U.Id],Current_Id,Previous_Id,[Date])), --A column with a . in the name is a bad idea.
--Date is an odd name for something that is clearly an int
--Solution
rCTe AS(
SELECT YT.[U.Id],
YT.Current_Id,
YT.Previous_Id,
YT.[Date],
YT.Current_Id AS Start_Id
FROM YourTable YT
WHERE Previous_ID IS NULL
UNION ALL
SELECT YT.[U.Id],
YT.Current_Id,
YT.Previous_Id,
YT.[Date],
r.Start_Id
FROM YourTable YT
JOIN rCTE r ON YT.Previous_Id = r.Current_Id)
SELECT r.[U.Id],
r.Start_Id AS Current_Id,
MIN(r.[Date]) AS StartDate,
MAX(r.[Date]) AS EndDate
FROM rCTE r
GROUP BY r.[U.Id],
r.Start_Id;

Related

Postgres Crosstab on double columns with unknown value

So i have a table like this in my Postgres v.10 DB
CREATE TABLE t1(id integer primary key, ref integer,v_id integer,total numeric, year varchar, total_lastyear numeric,lastyear varchar ) ;
INSERT INTO t1 VALUES
(1, 2077,15,10000,2020,9000,2019),
(2, 2000,13,190000,2020,189000,2019),
(3, 2065,11,10000,2020,10000,2019),
(4, 1999,14,2300,2020,9000,2019);
select * from t1 =
id ref v_id total year total_lastyear lastyear
1 2077 15 10000 2020 9000 2019
2 2000 13 190000 2020 189000 2019
3 2065 11 10000 2020 10000 2019
4 1999 14 2300 2020 9000 2019
Now i want to Pivot this table so that i have 2020 and 2019 as columns with the total amounts as values.
My Problems:
I don't know how two pivot two columns in the same query, is that even possibly or do you have to make two steps?
The years 2020 and 2019 are dynamic and can change from one day to another. The year inside the column is the same on every row.
So basicly i need to save the years inside lastyear and year in some variable and pass it to the Crosstab query.
This far i made it myself but i only managed to pivot one year and the 2019 and 2020 years is hardcoded.
Demo
You can pivot one at a time with WITH.
WITH xd1 AS (
SELECT * FROM crosstab('SELECT ref,v_id,year,total FROM t1 ORDER BY 1,3',
'SELECT DISTINCT year FROM t1 ORDER BY 1') AS ct1(ref int,v_id int,"2020" int)
), xd2 AS (
SELECT * FROM crosstab('SELECT ref,v_id,lastyear,total_lastyear FROM t1 ORDER BY 1,3',
'SELECT DISTINCT lastyear FROM t1 ORDER BY 1') AS ct2(ref int,v_id int,"2019" int)
)
SELECT xd1.ref,xd1.v_id,xd1."2020",xxx."2019"
FROM xd1
LEFT JOIN xd2 AS xxx ON xxx.ref = xd1.ref AND xxx.v_id = xd1.v_id;
This doesn't prevent from last_year and year colliding.
You still have to know the years query will return as you have to define record as it is returned by crosstab.
You could wrap it in an EXECUTE format() to make it more dynamic and deal with some stringology.
This issue was mentioned here.

How to Outer Join a Calendar table to view dates with 0 records

I have a table with records of orders by customers and a table with dates from Jan 2022 to 10 years. I wanted to get all numbers of customers made everyday for the last 28 days, including those with 0 customers recorded. So I needed to outer join the calendar table to the customer records. However, I cant use outer join correctly.
Here's how I done it:
SELECT order_date as 'date', COUNT(orderstatus) as 'customers'
FROM orders
RIGHT OUTER JOIN calendar ON
calendar.date = orders.order_date
WHERE sellerid = 11
Im getting:
date customers
2022-01-02 9
I wanted to see:
date customers
2022-01-01 0
2022-01-02 9
2022-01-03 0
.
.
.
You would not get the results that you posted in your question unless you group by date, so I guess you missed that part of your code.
You need a WHERE clause to filter the calendar's rows for the last 28 days and you must move the condition sellerid = 11 to the ON clause:
SELECT c.order_date,
COUNT(o.order_date) customers
FROM calendar c LEFT JOIN orders o
ON o.sellerid = 11 AND o.order_date = c.date
WHERE c.date BETWEEN CURRENT_DATE - INTERVAL 28 DAY AND CURRENT_DATE
GROUP BY c.order_date;

Select first non-null value for multiple columns and different rows in PostgreSQL

I'm trying to create a view based off a table. I want to get a set of rows where there is an existing tax_id_no, with each row having the most recent information. So I'm ordering by timestamps descending. However, each tax_id_no can have multiple rows, and not every row will have all the information. So I want to get the first valid piece of information for each column. Right now I've got this:
SELECT * FROM
(
SELECT DISTINCT ON (store_id, tax_id_no)
event_id,
event_tstamp,
owner_id,
store_id,
tax_id_no,
first_value(year_built) OVER (ORDER BY year_built IS NULL, event_tstamp) AS year_built, --New
first_value(roof_replaced_year) OVER (ORDER BY roof_replaced_year IS NULL, event_tstamp) AS roof_replaced_year, --New
first_value(number_of_rooms) OVER (ORDER BY number_of_rooms IS NULL, event_tstamp) AS number_of_rooms, --New
FROM MySchema.Event
WHERE tax_id_no IS NOT NULL AND tax_id_no != ''
order by store_id, tax_id_no, event_tstamp DESC
) t1
WHERE owner_id IS NOT NULL OR owner_id != '';
This is getting the same first valid information for every row though. So instead of getting results like this, which is what I want:
event_id event_tstamp owner_id store_id tax_id_no year_built roof_replaced_year number_of_rooms
04 2016-05-12 123 02 12345 1996 2009 6
05 2017-02-02 245 02 23456 1970 1999 8
08 2017-03-03 578 03 34567 2002 2016 10
I'm getting this, which all the rows looking the same in the first_value columns:
event_id event_tstamp owner_id store_id tax_id_no year_built roof_replaced_year number_of_rooms
04 2016-05-12 123 02 12345 1996 2009 6
05 2017-02-02 245 02 23456 1996 2009 6
08 2017-03-03 578 03 34567 1996 2009 6
Is it possible to select a different first_value for each row? I was thinking I could do some kind of a join across multiple selects from the same table, but I'm not sure that would actually give me unique values for each row instead of just having the same problem again. There's also the length of time for such queries to consider, which so far have been prohibitively expensive.
You can use a partition in your window functions to group the rows before applying the function. That will generate a distinct result for each partition.
For example:
first_value(number_of_rooms) OVER (
PARTION BY tax_id_no
ORDER BY number_of_rooms IS NULL, event_tstamp
) AS number_of_rooms,

Not getting desired format from Oracle query

I am trying to fetch data from data base in below format,
Month Count
----- -----
201208 124
201209 0
201210 56
201211 25
201212 0
201301 184
201302 0
In database I have entries like,
Month Count
----- -----
201206 56
201208 124
201210 56
201211 25
201301 184
201304 49
Below is my query,
SELECT MONTH, Count
FROM TABLE_NAME
WHERE MONTH BETWEEN 201208 AND 201302
AND ID = 'X'
Output :
Month Count
----- -----
201208 124
201210 56
201211 25
201301 184
Can anyone help me getting data in desired format.
First you should generate full month's sequence between these dates. You can do it with CONNECT BY LEVEL in Oracle. then just JOIN this sequence with your table:
SELECT MonthSeq.MONTH,
NVL(Count,0) Count
FROM TABLE_NAME
RIGHT JOIN
(
SELECT
TO_CHAR(ADD_MONTHS(TO_DATE('201208','YYYYMM'),
(ROWNUM-1))
,'YYYYMM') MONTH
FROM DUAL
CONNECT BY LEVEL<=
MONTHS_BETWEEN(TO_DATE('201302','YYYYMM') ,
TO_DATE('201208','YYYYMM'))+1
) MonthSeq
ON TABLE_NAME.MONTH=MonthSeq.MONTH
ORDER BY MonthSeq.MONTH
SQLFiddle demo
UPD:
Your query from the comment should looks like the following. You should move WHERE condition to the JOIN ON. If you use it in WHERE you don't get rows with zero counts.
SELECT MonthSeq.MONTH,
NVL(SUM(TOTAL_SESSIONS),0) AS SESSIONS
FROM X
RIGHT JOIN
(
SELECT
TO_CHAR(ADD_MONTHS(TO_DATE('201208','YYYYMM'),
(ROWNUM-1))
,'YYYYMM') MONTH
FROM DUAL
CONNECT BY LEVEL<=
MONTHS_BETWEEN(TO_DATE('201302','YYYYMM') ,
TO_DATE('201208','YYYYMM'))+1
) MonthSeq
ON X.MONTH=MonthSeq.MONTH and X.acct_id = 'ABCD'
ORDER BY MonthSeq.MONTH
You need to use TO_DATE function to convert the month field to DATE format. Refer here for more in detail. Try like this,
SELECT TO_CHAR(TO_DATE(MONTH, 'YYYYMM'), 'YYYYMM') month, count
FROM TABLE_NAME
WHERE month BETWEEN TO_DATE('201208', 'YYYYMM') AND TO_DATE('201302', 'YYYYMM')
AND id = 'X'
ORDER BY TO_DATE(month, 'YYYYMM');

get result for table having two foreign key of same table

I have two table suppose table 'friendship' with column f1,f2 and 'people' with column p_id,p_name.
firendship:=>
f1 f2
--------
01 10
02 11
03 12
People:=>
p_id p_name
------------
01 Vijay
02 Ajay
03 Gaurav
10 Sunny
11 Amit
12 Sandeep
and i want result like this table
Result Table should be:=>
f1 f1_name f2 f2_name
------------------------
01 Vijay 10 Sunny
02 Ajay 11 Amit
03 Gaurav 12 Sandeep
i try with Union two tables but can't found accurate result
SELECT a.p_id f1, a.p_name f1_name, b.p_id f2, b.p_name f2_name FROM People a JOIN friendship f ON (f.f1 = a.p_id) JOIN People b ON (f.f2 = b.p_id)
You need Join for this
select f1, p.p_name as f1_name , f2, p1.p_name f2_name
from firendship as f
inner join People as p on f.f1=p.p_id
inner join People as p1 on f.f2=p1.p_id
I think this can solve your problem.
select a.f1, c.p_name, b.f2, c.p_name
(select * from tablea) as a,// which have f1 and f2 columns
(select * from tablea) as b,
(select * from people) as c
where a.f1=c.p_id and b.f2=c.p_id