Snowflake - query to find roles that haven't been used recently - snowflake-cloud-data-platform

I am trying to delete roles in Snowflake that haven't been used recently.
Is there a way to query the last date a role was used to execute a query? query_history seems promising, but according to the docs, it only allows query_history_by_user.

You can get that from the SNOWFLAKE database. Just keep in mind that the data in the Snowflake database is up to 3 hours delayed, but most views are about 15-20 minutes or so delayed:
-- This shows all roles used in the last 30 days
select ROLE_NAME
from SNOWFLAKE.ACCOUNT_USAGE.QUERY_HISTORY
where END_TIME >= dateadd(day, -30, current_timestamp)
group by 1
;
-- This shows all roles:
select NAME as ROLE_NAME from SNOWFLAKE.ACCOUNT_USAGE.ROLES where DELETED_ON is null
;
-- Combine the two and join to get unused roles in the last 30 days:
with USED_ROLES as
(
select ROLE_NAME
from SNOWFLAKE.ACCOUNT_USAGE.QUERY_HISTORY
where END_TIME >= dateadd(day, -30, current_timestamp)
group by 1
), ROLES as
(
select NAME as ROLE_NAME from SNOWFLAKE.ACCOUNT_USAGE.ROLES where DELETED_ON is null
)
select ROLE_NAME from ROLES where ROLE_NAME not in (select ROLE_NAME from USED_ROLES)

Here's a solution that returns the role name and the last date that it was used. Note that the query_history view only has data for the last 365 days, so you cannot return a date for roles that haven't been used in > 1 year:
with RECENTLY_USED as (
select ROLE_NAME, max(END_TIME) as last_query_date
from SNOWFLAKE.ACCOUNT_USAGE.QUERY_HISTORY
where END_TIME >= dateadd(day, -30, current_timestamp)
group by 1
),
LAST_YEAR as (
select ROLE_NAME, max(END_TIME) as last_query_date
from SNOWFLAKE.ACCOUNT_USAGE.QUERY_HISTORY
where END_TIME >= dateadd(year, -1, current_timestamp)
group by 1
)
select ROLE_NAME, LAST_QUERY_DATE from LAST_YEAR
minus
select ROLE_NAME, LAST_QUERY_DATE from RECENTLY_USED
order by LAST_QUERY_DATE;
Output:
ROLE_NAME
LAST_QUERY_DATE
My_old_role
2022-04-21T11:44:38.384-05:00
My_other_old_role
2022-06-07T15:14:45.245-05:00

Related

How to do flaten a date sequence in snowflake given the 'unnest' code in Athena SQL?

I am just learning Snowflake. Now having a problem which is probably about unnest/flaten etc. I have a SQL code in Athena:
SELECT
CAST(a2 AS DATE) dates
FROM
(VALUES
(SEQUENCE(CAST(FROM_ISO8601_DATE('2022-01-01') as timestamp),
CAST(CURRENT_TIMESTAMP as timestamp),
INTERVAL '1' day)
)
) AS t1(a1)
CROSS JOIN
UNNEST(a1) AS t2(a2);
The results is a date sequence:
How can I do this in Snowflake?
WITH GAPLESS_ROW_NUMBERS AS(
SELECT ROW_NUMBER() OVER (ORDER BY seq4()) - 1 as "ROW_NUMBER"
FROM TABLE(GENERATOR(rowcount => 400))) -- 400 OR 400000 BOTH WORK FAST
SELECT DATEADD('DAY', ROW_NUMBER,'2022-01-01')::DATE as DATE --YOU CAN ADJUST '2022-01-01'
FROM GAPLESS_ROW_NUMBERS
WHERE DATE BETWEEN '2022-01-01' AND CURRENT_DATE();

Query to count living population each year

I have a table with a list of databases, about 10k rows, 1 row for each database. The table contains attributes for:
ServerId (int)
DBID (int)
DBName (varchar(255))
Createtime(datetime)
LastCheckTime(datetime).
LastCheckTime is frequently updated until the database is removed on the SQL Server, and then it stops being updated.
I would like to count how many "living" databases there have been each year. If the LastCheckTime < getdate()-1 then I assume it's alive and should be counted for each year from Createtime until LastCheckTime is more than 1 day.
The table resides in a SQL Server 2008R2 database. I can upgrade to SQL Server 2016 if it's easier to accomplish this.
The following query returns the desired output:
CREATE TABLE [DB]
(
ServerId INT,
[DBID] INT,
DBName varchar(255),
Createtime DATETIME,
LastCheckTime DATETIME
)
INSERT INTO [DB] VALUES
(1,1, 'DB1', '2010-09-10', '2013-09-01'),
(1,2, 'DB2', '2010-09-10', GETDATE()),
(1,3, 'DB3', '2010-09-12', GETDATE()),
(1,4, 'DB4', '2011-09-13', GETDATE()),
(1,5, 'DB5', '2011-09-10', GETDATE()),
(1,6, 'DB6', '2013-09-10', GETDATE()),
(1,7, 'DB7', '2014-09-10', GETDATE()),
(1,8, 'DB8', '2015-09-10', GETDATE()),
(1,9, 'DB9', '2016-09-10', GETDATE()),
(1,10, 'DB10', '2017-09-10', GETDATE());
--CTE holds all the years starting from the oldest DB
;WITH CTE
AS ( SELECT DATEPART(YEAR, ( SELECT MIN(CreateTime)
FROM [DB]
)) AS year
UNION ALL
SELECT year + 1
FROM CTE
WHERE year < DATEPART(YEAR, GETDATE())
)
SELECT year ,
COUNT(*) DBCount
FROM CTE
LEFT JOIN [DB] ON CTE.year BETWEEN DATEPART(YEAR, [DB].Createtime)
AND DATEPART(YEAR,
[DB].LastCheckTime + 1)
WHERE [DB].LastCheckTime > DATEADD(DAY, -1, GETDATE()) -- filter only live DBs
GROUP BY year;
And the result would be:

Select all days of the current week

Good Day! I am working on a chart where I need to display all the days of the current week to show the sales per Week. So far, I am able to display all the days of the current week, I'm just having a trouble in displaying the sales for each day of the week.Since there are no records in the database for the days of the week, it the TOTAL_SALES column should all return a Null value. Instead, it returns the total sales recorded in the database. Here is my Stored Procedure query so far.
WITH DAYSOFTHEWEEK AS
(
SELECT 0 DAY
UNION ALL
SELECT DAY + 1 FROM DAYSOFTHEWEEK WHERE DAY < 6
)
SELECT DATEADD(DAY, DAY, DATEADD(DAY, 2-DATEPART(WEEKDAY, CONVERT (date, GETDATE())), CONVERT (date, GETDATE()))) AS DAY_OF_THE_WEEK,
SUM([ORDER].NET_AMOUNT) AS TOTAL_SALES
FROM DAYSOFTHEWEEK, [ORDER]
GROUP BY DAYSOFTHEWEEK.DAY
I tried adding this condition statement,
WHERE DAYSOFTHEWEEK.DAY IN ([ORDER].ORDER_DATE)
But it returns this error
Operand type clash: date is incompatible with int
Can someone help me out on this?Is there a work around with the code that I already have? Thanks in advance!
What I think you're after is a SUM of each day's sales for the current week with NULL if there are no sales. The secret is to left join your date list onto your data:
-- Setup some fake sales data
WITH TestData(N, Order_Date, Net_Amount) AS (
SELECT 1 N, CAST(GETDATE() AS DATE) Order_Date, RAND() * 100 Net_Amount
UNION ALL
SELECT N+1 N, CAST(GETDATE()-N/5 AS DATE) Order_Date, RAND(CHECKSUM(NEWID())) * 100 Net_Amount FROM TestData
WHERE N < 20
)
SELECT TestData.Order_Date, TestData.Net_Amount INTO #Order FROM TestData
--Set the first day of the week (if required)
SET DATEFIRST 7 --Sunday
;WITH Days(N,DayOfTheWeek) AS (
SELECT 1 N, DATEADD(DAY, 1-DATEPART(WEEKDAY, GETDATE()), CONVERT(DATE,GETDATE())) DayOfTheWeek
UNION ALL
SELECT N+1 N,DATEADD(DAY, 1, DayOfTheWeek) DayOfTheWeek FROM Days
WHERE N < 7
)
SELECT d.DayOfTheWeek, SUM(Net_Amount) TotalAmount
FROM Days d
LEFT JOIN #Order ON d.DayOfTheWeek = Order_Date
GROUP BY d.DayOfTheWeek
DayOfTheWeek TotalAmount
------------ ----------------------
2016-08-07 219.036784917497
2016-08-08 273.319570812461
2016-08-09 271.148114731087
2016-08-10 194.780039228967
2016-08-11 NULL
2016-08-12 NULL
2016-08-13 NULL
Here is every day this week, starting at your datefirst date, which can be temporarily varied for the query with SET DATEFIRST if you need to have some other week start date
I think you have some sales table there that you haven't shown us, you need to join to that on date, then group by
WITH DAYSOFTHEWEEK AS
(
SELECT cast(dateadd(
day,
-datepart(weekday,getdate()) + 1 ,
GETDATE()
)
as date) [DAY], 0 as cnt
UNION ALL
SELECT dateadd(day,1,[DAY]), cnt + 1 FROM DAYSOFTHEWEEK WHERE cnt < 6
)
select DAYSOFTHEWEEK.[day], SUM([ORDER].NET_AMOUNT) AS TOTAL_SALES from daysoftheweek
JOIN
SalesTable on
CAST(SalesTable.SalesDate date) = DAYSOFTHEWEEK.[day]
GROUP BY DAYSOFTHEWEEK.[day]
A little over complicated for me:
To get name of the week use, for example
SELECT DATENAME(dw,getdate())
But you really need something like this:
SELECT ProductName,Sum(Sales) From NameOfTable GROUP BY
DATENAME(ww,salesDate)

Group By Month and Year to get distinct month and years then combine as a single date SQL

SELECT CONVERT(DATE,CAST([Year] AS VARCHAR(4))+'-'+
CAST([Month] AS VARCHAR(2))+'-'+
CAST('1' AS VARCHAR(2))) Date
FROM (SELECT YEAR(Date) as [Year], MONTH(Date) as [Month] FROM [dbo].[Data]
GROUP BY YEAR(Date), MONTH(Date)) x
ORDER BY Date DESC
Is there a better way to doinq this with a single query?
The query should return the unique month and year from a table but as combined Date.
IF OBJECT_ID ('tempdb..#TempT') IS NOT NULL DROP TABLE #TempT
CREATE TABLE #TempT(
dt datetime)
INSERT INTO #TempT (dt) VALUES
('2016-10-11'),
('2016-10-3'),
('2016-9-13'),
('2016-9-16')
SELECT DISTINCT CAST(DATEADD(month, DATEDIFF(month, 0, dt), 0) as DATE) AS Dates
from #TempT
Sample data is really a necessity for these kinds of questions, but this function could also be a help for you DATEFROMPARTS
SELECT
DATEFROMPARTS([Year], [Month], 01)
FROM
(
SELECT
YEAR(Date) as [Year],
MONTH(Date) as [Month]
FROM [dbo].[Data]
GROUP BY YEAR(Date), MONTH(Date)
) x
ORDER BY Date DESC
This code takes whatever date you enter and evaluates it as the first of the month. Effectively looking at only month and year:
SELECT
DATEADD(MM,DATEDIFF(MM,0, [Date] ),0) AS [YearMonth]
FROM [dbo].[Data]
ORDER BY
DATEADD(MM,DATEDIFF(MM,0, [Date] ),0)
;

SQL Query - Year by Year Growth of Sales

I have this result set from an SQL query. This result has come up by grouping the sales of tenant per year to get the total sales in yearly basis. Table is named TENANTSALES with columns: Tenant, date, sales etc.
TENANT YEAR SALES
tenant 1 2014 2000
tenant 1 2015 5000
tenant 2 2013 1000
tenant 2 2014 1500
tenant 2 2015 800
I used this SQL query code to achieve the above result
select tenant, year(date), SUM(sales)
from tenantSales
group by tenant, YEAR(date)
What I need to complete the task is to add a column name Yearly growth where it will compare and compute for year by year growth of sales per tenant. Here's the sample correct / desired output
TENANT YEAR SALES YEARLY GROWTH
tenant 1 2014 2000
tenant 1 2015 5000 150%
tenant 2 2013 1000
tenant 2 2014 1500 50%
tenant 2 2015 800 -46.67%
The formula is: ((Latest Year - Previous Year) / Previous Year) * 100
Example for Tenant 1:
((2015 sales - 2014 sales) / 2014 sales) * 100 = 150%
Ive tried to do this, adding the next year in the previous year's row to make it easy for me to compute for the two years sales, but I can't add up the sales of the latest year, only the year itself. Is there any way or proper way to do it?
select tenantcode, year(date), SUM(gsc), year(date) + 1
from dailymod
where tenantcode = 'cmbina13'
group by tenantcode, YEAR(date)
Your expert advice will be highly appreciated. Thanks
Try this query:
SELECT t1.tenant, t1.YEAR, t1.SALES,
CASE WHEN t2.YEAR IS NOT NULL THEN
FORMAT(
CONVERT(DECIMAL(10, 2), (t1.SALES - t2.SALES)) /
CONVERT(DECIMAL(10, 2), t2.SALES), 'p')
ELSE NULL END AS "YEARLY GROWTH"
FROM
(
SELECT tenant, YEAR(date) AS YEAR, SUM(sales) AS SALES
FROM tenantSales
GROUP BY tenant, YEAR(date)
) t1
LEFT JOIN
(
SELECT tenant, YEAR(date) AS YEAR, SUM(sales) AS SALES
FROM tenantSales
GROUP BY tenant, YEAR(date)
) t2
ON t1.tenant = t2.tenant AND t2.YEAR = t1.YEAR - 1
Click the link below for a working demo:
SQLFiddle
Late Update:
You could also try the same approach using Common Table Expressions. Here is what the above query would look like using this approach:
WITH cte AS(SELECT tenant, YEAR(date) AS YEAR, SUM(sales) AS SALES
FROM tenantSales
GROUP BY tenant, YEAR(date))
SELECT c1.*, CONVERT(varchar,
CONVERT(DECIMAL(10,2),
CONVERT(DECIMAL(10, 2), (c1.SALES - c2.SALES)) /
CONVERT(DECIMAL(10, 2), c2.SALES))) + '%' AS "YEARLY GROWTH"
FROM cte c1
LEFT JOIN cte c2 ON c1.tenant = c2.tenant AND c2.YEAR = c1.YEAR - 1
And here is another Fiddle where you can test this out:
SQLFiddle
Using cte you can reuse your query. I am using window function because you can have some missed years. But if years are consecutive then you can join directly on year column:
with cte as(select tenant,
year(date) y,
SUM(sales) s,
row_number() over(partition by tenant order by sum(sales)) rn
from tenantSales
group by tenant, YEAR(date))
select c1.*, ((c1.s - c2.s) / c2.s) * 100 as grouth
from cte c1
left join cte c2 on c1.tenant = c2.tenant and c1.rn = c2.rn + 1
Or:
with cte as(select tenant, year(date) y, SUM(sales) s
from tenantSales
group by tenant, YEAR(date))
select c1.*, ((c1.s - c2.s) / c2.s) * 100 as grouth
from cte c1
left join cte c2 on c1.tenant = c2.tenant and c1.y = c2.y + 1
please consider my answer with analytical function.
select tenant ,year(date) ,sum(sales),
format( ((sum(sales)/convert(float,lag(sum(sales)) over( partition by tenant order by tenant,year(date))))-1),'p')
from tenantSales
group by tenant , year(date)
select tenant_id
,year
,sales
, round((case when chk<>0 then ((sales-chk)/chk)*100 else 0 end),2)as yoy
from(
select tenant_id
,year
,sales
,lag(sales,1,0) over(partition by tenant_id order by year asc) as chk
from tenant)
Please find the below solution.
create table tenant_details
(tenant varchar(10),
year number(10),
sales number(10)
);
insert into tenant_details values ('tenant 1',2014,2000);
insert into tenant_details values ('tenant 1',2015,5000);
insert into tenant_details values ('tenant 2',2013,1000);
insert into tenant_details values ('tenant 2',2014,1500);
insert into tenant_details values ('tenant 2',2015,800);
commit;
SQL Query:
select tenant,year,sales,case when prev_sales is null then null else
to_char((sales-prev_sales)*100/prev_sales,9999.9) ||'%' end profit
from (
select tenant,year,sales ,lag(sales,1) over (partition by tenant order by year) prev_sales from tenant_details
);

Resources