SQL REDSHIFT - Unpivot fields and group by a split function without joining - sql

I have a data set structure similar to below.
Product Group
Item
Sunday_Capacity
Monday_Capacity
Tuesday_Capacity
etc.
Product GroupA
Item A
10
8
5
I would like to get a resulting data set that was Product Group | Item | Day of Week | Capacity.
I realize I could do this with a join for each day but would like to unpivot this information. I imagine I will have to dynamically split the day of week value with a spilt function.

Related

Select same account numbers in a new table

Using Teradata SQL Assistant, I want to be able to pull a table a year ahead but only the ones that would match the results in the query from the year before. Here's what I am trying to do. I pulled a table that contains information where the results in a specific column equals 0 for no. I want to pull information from 1 year ahead where the results in that column equals 1 but only include the account numbers that came when I pulled the results for the year before. Like only pull the customer account numbers for the year ahead that are the same from the year before.
Explanation: I pull the one table that has 0 in the column. From that, I want to see which of those accounts became a 1 in the table from a year ahead. The table has millions of accounts and I just have my settings for 10,000 of them so I want to see of those 10,000 in the first year that did not have the product, how many of them became 1 in the second year.
Can I do this? If so, how? I have been googling and I do not think I am explaining what I am trying to do correctly in my google query so I am coming up short with results.
Thanks for clarifying. That makes it a little simpler. I would put the second year data in a subquery and filter the main table on the first year and quantity = 0. This will give you two columns one with the first year and one with the second year. If you're only looking for this information for a single product_id you will need to add this to both WHERE clauses.
SELECT TABLE_NAME.ACCOUNT_ID, TABLE_NAME.QUANTITY AS "2019" , YEAR_TWO.QUANTITY AS "2020"
FROM TABLE_NAME
LEFT JOIN
(
SELECT *
FROM TABLE
WHERE YEAR = 2020
) YEAR_TWO ON TABLE_NAME.ACCOUNT_ID = YEAR_TWO.ACCOUNT_ID
WHERE TABLE_NAME.YEAR = 2019
AND TABLE_NAME.QUANTITY = 0
If you want just the % of accounts that are no longer 0 in the second year you could try something like this (adding up all the 1s and dividing by total count)
SELECT TABLE_NAME.YEAR, SUM(YEAR_TWO.QUANTITY) / COUNT(YEAR_TWO.QUANTITY) AS PERCENTAGE_NOT_ZERO
FROM TABLE_NAME
LEFT JOIN
(
SELECT *
FROM TABLE
WHERE YEAR = 2020
) YEAR_TWO ON TABLE_NAME.ACCOUNT_ID = YEAR_TWO.ACCOUNT_ID
WHERE TABLE_NAME.YEAR = 2019
AND TABLE_NAME.QUANTITY = 0
GROUP BY TABLE_NAME.YEAR

Best practice for saving a series of dates in SQL

I'm reworking some old programs and in one of them I need so save a repeating series of Dates in the database. The User picks days ranging from 1-31 and months ranging from 1-12 in a PHP-Form. Multiple Choices are possible. At least one of each must be provided.
I'll then use a daily scheduled Task to check if the value (day and month) is given and if yes - do something.
In the old system I saved it like this:
| Days | Months |
|1,2,5,13,15 | 1,2,3,4,5,6,7,8,9,10,11,12|
Then I exploded every row in the PHP-File fired by the scheduled Task and iterated over the Array. If one of the dates is valid - do something.
What is best practice for this Use-Case? I thought about some solutions like "saving all possible Outcomes of days and months as single rows in an mapping-table" but I don't think that's an elegant solution...and it needs to be editable too after being implemented.
Any suggestions?
I think you're looking at three tables.
Table one records the groups, give it a sequential group id and whatever other properties you need to record about the group of dates as a whole (requesting user id).
Second table is just group id from table one and the chosen days in rows, so each group has multiple rows.
Third table is the same as for second but for months.
When you need the final result join the second and third tables to the first on the group id. you'll automatically get a cross join between the two giving the combinations you need.
If you're expecting a large volume of data and\or a lot of repeats of the same groups then you may want to consider the possibility of re-using the groups of days and months. It will be a similar table design but tables 2 and 3 will have their own group ids and table one will have two extra columns one for day group and one for month group.
Seems, you can use a dimension-like scheme and attach day-month pairs to different entities. Suppose, the entity is called "task".
| tasks | | days | | months |
| ------- | | -------- | | -------- |
| id_task | | id_day | | id_month |
| ... | >---M:1--- | id_month | >---M:1--- | month |
| id_day | | day |
Don't forget to add check constraints for day (1-31) and month (1-12) columns.
I think you should expand the data in the database. Clearly, you need a table groups (or something like that) with one row per group:
create table groups (
group_id int identity(1, 1) primary key,
. . . -- additional columns
);
Then, expand the dates for each group for the schedule:
create table groups_schedule (
group_schedule_id int identity(1, 1) primary key,
group_id int references groups(group_id),
month int,
day int
);
This requires multiplying out the data in the database. However, I think it is a more accurate representation. In addition, it will give you more flexibility in the future so you are not tied specifically to lists of months/days. For instance, you might have day "25" in most months, but not December.

Stacking column data in excel SQL query

I'm trying to pull data from an access database into an excel table with an SQL query. The problem is that my access database has columns with similar data that I want to combine into one single column. This should give me duplicates of the data in other columns for each entry. I'm not great with SQL but I think I have the basics down.
Database structure that I have:
Date | Product | Hours 1 | Reason 1 | Hours 2 | Reason 2 |
2019 A 3 "xxx" 5 "yyy"
Excel table that I want:
Date | Product | Hours | Reason |
2019 A 3 "xxx"
2019 A 5 "yyy"
Also not sure if it's possible but it would be great to see the source column of each
Date | Product | Hours | Reason | Source |
2019 A 3 "xxx" "Hours 1"
2019 A 5 "yyy" "Hours 2"
I've tried UNION ALL and got duplicates of the data but not merged into one column. I'm about to try INSERT INTO but sort of lost on how to get each one into the same column
Try this
SELECT Date, Product, Hours, Reason, Source
FROM (
SELECT Date, Product, Hours1 Hours, Reason1 Reason, "Hours 1" Source
FROM Table
UNION
SELECT Date, Product, Hours2, Reason2, "Hours 2"
FROM Table
)
It looks like you have a bad data structure in the table. By that I mean its a "flat" table with multiple hours in one row for a record. This is generally a PITA when it comes doing tasks for reviewing data in many to one situations. Normally there would be a table where records get logged separately for each hour involved. I understand you probably didnt build it, but its worth pointing out for you own information.
Fundamentally, this issue would be easier to appproach once you understood how that less than desirable structure affects what youre task is. This is essentially, in my mind, a pivot problem. PIVOT in SQL is essentially switching rows and columns. There are may ways to pivot data with code - pick your favorite - most people actually use the function PIVOT, where I tend to teeter between CTE's (common table experessions) and PIVOT. IMO CTE's are easier to read once you understand them. Because Acess SQL doesnt support PIVOT or CTE's we just had to treat the body of what a cte wouldve been as a correlated subquery.
SELECT x.*
FROM
(
SELECT
Date,
Product,
Hours,
Reason,
[Hours 1] AS Source
FROM yourTableName
UNION
SELECT
Date,
Product,
Hours,
Reason,
[Hours 2] AS Source
FROM yourTableName
) x

Oracle SQL returns error when selecting more than one field without an aggregate expression

I am working with SQL queries and I have a table with months and prices and I am performing a Moving Average. My code is as follows:
SELECT month, prices,
round(AVG(SUM(prices)) OVER
(ORDER BY month ROWS BETWEEN 1 PRECEDING AND CURRENT ROW),2)
AS MA
FROM tblma
GROUP BY month
ORDER BY month;
Although to me it seems right obviously I am making a mistake I don't see.
My retuned error is: 00979. 00000 - "not a GROUP BY expression"
My original data has the following format:
ID | MONTH | PRICE
1 | 11 | 20.36
2 | 12 | 11.05
Basically I would like to add the Moving Average column at the right.
This is probably what you want, the Moving Average over the Summmed prices:
SELECT month, SUM(prices),
round(AVG(SUM(prices)) OVER
(ORDER BY month ROWS BETWEEN 1 PRECEDING AND CURRENT ROW),2)
AS MA
FROM tblma
GROUP BY month
ORDER BY month;
The problem is that you are trying to select prices but without an aggregate expression, or grouping by the price. If you remove the prices and just do SELECT month, round(AVG(SUM(prices)) something will be returned. The reason that you cannot select prices as well is if you have more than one price for the same month then you haven't specified how to deal with this.
Also, round(AVG(SUM)) will just return the rounded SUM of the values. It will first sum everything up, and then take the average of that number, which will be the number itself.

SSAS Row Count Aggregation

Hi I have a table like this:
idCustomer | idTime | idStatus
---------------------------------
1 | 20010101 | 2
1 | 20010102 | 2
1 | 20010103 | 3
2 | 20010101 | 1
...
I have now added this table as a factless fact table in my cube with a measure which aggregates the row count for each customer, so that for each day I can see how many customers are at each status and I can drill down to see which customers they are.
This is all well and good but when I roll it up to the month or year level it start summing up the values of each day where instead I want to see the last non empty value.
I'm not sure if this is possible but I can't think of another way of getting this information without creating a fact table with the counts for each status on each day and loosing the ability to drill down.
Can anyone help??
An easy way to get what you want would be to convert your factless fact table to one having a fact: the count. Just add a named calculation to the table object in the data source view. Name the calculation like you want your measure to be named, and use 1 as the expression. Then you can define a measure based on this calculation using the aggregate function "LastNonEmpty", and use this instead of your current count measure.