Looping/iterating based on table names in SQL Server - sql

Currently I have 2 tables in SQL Server: tab_master (current month table) and tab_08_2022 (indicating it refers to Aug data based on the timestamp).
The tables have exactly structures, the only difference being they pertain to different months.
There will be more tables coming in for subsequent months: tab_08_2022, tab_09_2022, tab_10_2022 and so on.
I need to count the number of rows for each flag (grouping by the flag column) and showcase them every month. This should also have the data from all the previous month's tables.
For one table the query could be:
select count(*), flag_col
from tab_master
group by flag_col;
When the second table comes in the query would be
select *
from
(select count(*), flag_col
from tab_master
group by flag_col) x
inner join
(select count(*), flag_col as flag_08_2022
from tab_09_2022
group by flag_col) y on x.flag = y.flag_08_2022;
When the third table comes in the query would be
select *
from
(select count(*), flag_col
from tab_master
group by flag_col) x
inner join
(select count(*), flag_col as flag_08_2022
from tab_08_2022
group by flag) y on x.flag = y.flag_08_2022
inner join
(select count(*), flag_col as flag_09_2022
from tab_09_2022
group by flag_col) z on x.flag_col = z.flag_09_2022;
How do I go about doing that?

Related

Select difference between two tables

I want to list four columns, date, hourly count, daily count and difference between two counts.
I have used union all for two tables, but I am getting 2rows as shown in the image:
Select a.date, a.hour,b.daily,sum(a.hour-b.daily)
from (select date,count(*) hour,''daily
From table a union all select '' hour,count(*) daily from table b)
Group by date, daily, hourly..
Please suggest to me a solution.
I see that the code supplied uses a UNION to achieve the output. This would be better served by using a JOIN of some kind.
The result is the total number of rows in table_a grouped by the date subtracted from the total number of rows in table_b grouped by the date.
This code is untested but should give a good indication of how to achieve this:
SELECT a.date,
a.hour,
ISNULL(b.daily, 0) AS daily,
a.hour - ISNULL(b.daily) AS difference
FROM (
SELECT date,
COUNT(*) AS hour
FROM table_a
GROUP BY date
) a
LEFT JOIN (
SELECT date,
COUNT(*) AS daily
FROM table_b
GROUP BY date
) b ON b.date = a.date
ORDER BY a.date;
This works by:
Calculating the count per date in table_a.
Calculating the count per date in table_b.
Joining all results from table_a with those matching in table_b.
Outputting the date, the hour from table_a, the daily (or 0 if NULL) from table_b, and the difference between the two.
Notes:
I have renamed table a and table b to table_a and table_b. I presume these are not the actual table names
An INNER JOIN may be preferable if you only want results that have matching date columns in both tables. Using the LEFT JOIN will return all results from table_a regardless of whether table_b has an entry.
I'm not convinced that date is an allowed column name but I have reproduced it in the code as per the example given by OP.
Your method is fine. Your group by columns are not correct:
Select date, sum(hourly) as hourly, sum(daily) as daily,
sum(hourly) - sum(daily) as diff
from ((select date, count(*) as hourly, 0 as daily
from table a
group by date
) union all
(select date, 0 as hourly, count(*) as daily
from table b
group by date
)
) ab
group by date;
The key idea is that the outer query aggregates only by date -- and you still need aggregation functions there as well.
You have other errors in your subquery, such as missing group bys and date columns. I assume those are transcription errors.

Ordering the results of Inner join

I currently have the following query:
select X.sellerID, x.Category, y.Award_Year, Y.Award
from Y
inner join
X on Y.Seller_ID=X.sellerID
Which give me this results:
How can I write a query to get Top 3 seller and the amount of awards recieved in 2017. Following is expected result:
I guess you want the top 3 sellers by descending order (i.e. most awards in top) and you could do a flashy pivot thing with filtering on year to get a more general query, but I suspect something like this one would be more suitable at this level..
The top 3 clause will differ between different DB Engines
MSSQL : TOP (nr) directly after SELECT
Oracle: FETCH FIRST nr ROWS ONLY last in statement
mysql: LIMIT nr last in statement
etc....
FOR MSSQL it would be something like
SELECT TOP (3) sellerID, Category, Award_Year, COUNT(Award_Year) Awards
from Y y
inner join
X x on y.Seller_ID=x.sellerID
WHERE Award_Year = 2017
GROUP BY x.sellerID, x.Category, y.Award_Year
Order By COUNT(Award_Year) DESC
Try this:
SELECT TOP(3) A.Id, A.category, count(A.Award) AS 'Award in 2017'
FROM (
SELECT X.sellerID AS ID, x.Category, y.Award_Year, Y.Award
FROM Y
INNER JOIN X on Y.Seller_ID=X.sellerID
) A
GROUP BY A.Id, A.category
ORDER BY count(A.Award) DESC
Depending on your actual DBMS version, you may need to amend it.

How to include missing rows in sql return

I am currently trying to do a query like this:
(Psuedocode)
SELECT
NAME, SUM(VALUE), MONTH
FROM TABLE
WHERE MONTH BETWEEN 12 MONTHS AGO AND NOW
GROUP BY MONTH, NAME
The problem I am getting is that a name exists in a few of the months, but not all of the months, so if i filter this down to return the values for only one name, i sometimes get only 3 or 4 rows, rather than the 12 I expect to see.
My question is, is there a way to return rows, where it will still include the name, and month within the range, where the value would just be set to zero when I am missing the row from the previous result.
My first thought was to just union another select onto it, but I cant seem to get the logic to work to adhere to the group by, as well as the where clauses for limiting the names.
I you have data for all months, you can take the following approach. Generate all the rows (uses a cross join) then bring in the data you want:
select m.month, n.name, sum(t.value)
from (select distinct month from table) m cross join
(select distinct name from table) n left join
table t
on t.month = m.month and t.name = n.name
group by m.month, n.name;
This will return the missing sums as NULL values. If you want zero, then use coalesce(sum(t.value), 0).
you can use something like the following table to generate all the past 12 months as separate rows:
SELECT add_months(trunc(add_months(sysdate, -12), 'MONTH'), LEVEL - 1) AS month_in_range
FROM all_objects
CONNECT BY LEVEL <= 1 + months_between(add_months(sysdate, -12), TRUNC (sysdate, 'MONTH'));
and then do an outer join between you table and this.
I ended up implementing a left outer join similar to #paqogomez 's comment. As my team is already maintaining a time table, its very easy to get the month list for an outer join.
SELECT NAME, SUM(VALUE), TIME.MONTH
FROM (SELECT DISTINCT MONTH FROM TIME_TABLE
WHERE MONTH BETWEEN 12 MONTHS AGO AND NOW) TIME
LEFT OUTER JOIN TABLE ON (TIME.MONTH = TABLE.MONTH)
GROUP BY TIME.MONTH, NAME

SQL GROUP BY ( DATEPART(), field1 ) result set to zero nulls

I want to aggregate counts, grouped by a datepart and column.
For example, a table with 3 columns with each row representing a unique event: id, name, date
I want to select total counts grouped by name and hour, with zeros when there are no events. If I'm only grouping by name, I can join it with a table of every name. With an hour I could do something similar.
How would I handle the case of grouping by both without having a table with a row for every name+hour combination?
The following is the mysql solution:
create table hours (hour int)
insert hours (hour) values (0), (1) .... (23)
select hour, name, sum(case when name is null then 0 else 1 end)
from hours left outer join
event on (hour(event.date) = hours.hour)
group by hour, name
the sum(case when name is null then 0 else 1 end) handles the case when there are no events for a particular hour and name. the count will show as 0. For others each matching row contributes 1 to the sum.
For sql server use datepart(hour, event.date) instead. The rest should be similar
You can use cross join to generate all the rows and then other logic to fill in the values:
select h.hour, n.name, count(a.name) as cnt
from (select distinct hour(date) as hour from atable) h cross join
(select distinct name from atable) n left join
atable a
on hour(a.date) = h.hour and a.name = n.name
group by h.hour, n.name;

How to join three select queries which has one common column

I have three select queries as below which gives a respective output
select DATE_FORMAT(table1.value_date,'%b')as Month,
DATE_FORMAT(table1.value_date,'%Y') as Year,
table1.open as Open
from index_main as table1
join ( select min(`value_date`) `value_date`
from index_main
group by month(`value_date`), year( `value_date`)
) as table2 on table1.`value_date` = table2.`value_date`
Output columns - Month,year,open
select DATE_FORMAT(table1.value_date,'%b')as Month,
DATE_FORMAT(table1.value_date,'%Y') as Year,
table1.close as Open
from index_main as table1
join ( select max(`value_date`) `value_date`
from index_main group by month(`value_date`), year( `value_date`)
) as table2 on(table1.`value_date` = table2.`value_date`)
Output columns - Month,year,close
select DATE_FORMAT(table1.value_date,'%b')as Month,
DATE_FORMAT(table1.value_date,'%Y') as Year,
max(table1.high) as High
FROM `index_main` as table1
GROUP BY table1.month,table1.year
ORDER BY year(table1.value_date) desc, month(table1.value_date) desc
Output columns - Month,year,high,low
I want to join these three select queries based on the common columns i.e month & year.
My final result should have the following columns - month,year,open,close,high,low.
Try this.
First create 3 views, one with each query (vw1, vw2 and vw3). Then use a query like this:
SELECT vw1.Month, vw1.Year, Open, Close High FROM vw1 LEFT join vw2 on vw1.Year=vw2.Year and vw1.Month=vw2.Month LEFT JOIN vw3 on vw1.Year=vw3.Year and vw1.Month=vw3.Month
Hope this helps you.