Modifying column value according to status, and adding extra rows. (sql) - sql

In my table I have a "name" column and a "status" column.
the status is either true or false.
And another table contains a number which is a total amount.
The result that I want to get is a table with two columns:
name | status
and and example of a data:
a | available
a | available
a | not available
a | not available
a | available
when "a" is in the name column and the availability is the status column.
The total amount from the second table indicates the total number of "a" rows that i need to have, and the status depends on the true/false from the status column in the original table.
If the status is "true" I need to write "available" and when "false" then "not available".
If the total amount value is bigger than the data I have in the first table, I need to add rows according to the total amount with the status "available".
For example, If I have 3 records of "a", when one has the status "true" and the other two have the status "false", and the total amount is 4, In the result I need to get 4 rows with the name "a", 2 of them "available" and 2 "not available" (the given 3 rows, plus one row to make it 4).
My question is, how can I change the value according to the data in the table? (Write available/ not available)
And how can I add a certain amount of rows with preset values (same name as before, and "available" status)?

"...how can I change the value according to the data in the table?"
You can use CASE() to test for the value of the column.
SELECT name,
CASE WHEN status = 'true'
THEN 'available'
ELSE 'not available'
END status
FROM tableName

For future answer-seekers:
To get the 'fake' rows, one way is to use are recursive CTE:
WITH Expanded_Data as (SELECT Counted_Data.name,
CAST('true' as VARCHAR(5)) as status,
Counted_Data.count + 1 as count,
Total.count as limit
FROM (SELECT name, COUNT(*) as count
FROM Data
GROUP BY name) Counted_Data
JOIN Total
ON Counted_Data.name = Total.name
AND Counted_Data.count < Total.count
UNION ALL
SELECT name, status, count + 1, limit
FROM Expanded_Data
WHERE count < limit)
SELECT name, CASE WHEN status = 'true'
THEN 'available'
ELSE 'not available' END
FROM (SELECT name, status
FROM Data
UNION ALL
SELECT name, status
FROM Expanded_Data) d;
(have a working SQL Fiddle example.)
I'm a little worried about the initial duplication in the source data though; I can only hope there is more 'unique' information as well.

Related

Query for checking if every cell is null in a column by id

Below is an example table, I am trying to figure out a way to write a query to put a Y in every cell under READY if all of the STOCK cells are empty for each ID row. So a Y would only show up in the READY columns for ID 1 since OS is in one of the columns for each other ID.
ID
STOCK
READY
1
1
1
2
OS
2
2
3
OS
3
My first thought is to use a case statement but that puts a Y in every column that has blank STOCK instead of checking if all of the STOCK for each ID is empty. Any ideas how I could accomplish this?
SELECT ID,
STOCK,
CASE WHEN STOCK = '' THEN 'Y'
ELSE '' END AS [READY]
FROM TABLE
You can use window functions:
select id, stock,
case when count(stock) over(partition by id) = 0 then 'Y' end as ready
from mytable
The window count takes in account non-null values of stock in rows sharing the same id.

need to pull a specific record

There is 1 record having duplicate values except in 1 column having x and y
record status
XXXXXXXXXX A
XXXXXXXXXX B
Need to pull A only and remove the other duplicate B
Select record
case
when status in ("'a', 'b'") then ('a')
from xyz
Let suppose you have data as below where Status is repeating for First column
but you are interesting in the status which is of having lower value as given below:
In this case following SQL may help. Here, we are partitioning on key field and ordering the Status so that we can apply filter on rank to get desired result.
WITH sampleData AS
 (SELECT '1234' as Field1,  'A' as STATUS UNION ALL 
  SELECT '1234',  'C' UNION ALL
  SELECT '5678', 'A' UNION ALL 
  SELECT '5678',  'B' )
 select * except(rank) from (
 select *, rank() over (partition by Field1 order by STATUS ASC) rank from sampleData)
 where rank = 1
 order by Field1
Consider below approach
select * from sampledata
qualify 1 = row_number() over win
window win as (partition by field1 order by if(status='A',1,2) )
if applied to sample data in your question - output is

Counting how many times one specific value changed to another specific value in order of date range and grouped by ID

I have a table like below where I need to query a count of how many times each ID went from specifically 'Waste Sale' in one value to 'On Stop' in the very next value based on ascending date and if there are no instances of this, the count will be 0
ID
Stage name
Stage Changed Date
1
Waste Sale
06-05-2022
1
On Stop
08-06-2022
1
Cancelled
09-02-2022
2
Waste Sale
06-05-2022
2
On Stop
07-05-2022
2
Waste Sale
08-06-2022
2
On Stop
10-07-2022
3
Cancelled
10-07-2022
3
On Stop
11-07-2022
The result I would be looking for based on the above table would be something like this:
ID
Count of 'Waste Sales to On Stops'
1
1
2
2
3
0
ID 1 having a count of 1 because there was one instance of 'Waste Sale' changing to 'On Stop' in the very next value based on date range
ID 3 having a count of 0 because even though the stage name changed to 'On Stop' the previous value based on date range wasn't 'Waste Sale'.
I have a hunch I would have to use something like LEAD() and GROUP BY/ ORDER BY but since I'm so new to SQL would really appreciate some help on the specific syntax and coding. Any version of SQL is okay.
We can use window function lead to take a peek at the next value of the query result.
select distinct id,
(
select count(*)
from
(
select *,
lead(stage_name)
over(
partition by id
order by stage_changed_date)
as stage_next
from sales s2
) s3
where s3.id = s1.id
and s3.stage_name = 'waste sale'
and s3.stage_next = 'on stop'
) as count_of_waste_sales_to_on_stop
from sales s1
order by id;
Query above uses lead(stage_name) over(partition by id order by stage_changed_date) to get the next stage_name in the query result while segregating it by id and order it based on stage_changed_date. Check the query on DB Fiddle.
Note:
I have no experience in zoho, so i'm unsure if the query will 100% works or not. They said it supported ansi-sql, however there might some differences with MySQL due to reasons.
The column names are not the exact same with op question due to testing only done using DB Fiddle.
There might better query out there waiting to be written.

SQL How to return a result set that will combine multiple rows into one row

I am trying to get one row returned for each store number and per date that includes all values from the RecordTypeA column for that date.
The table I am using is created with a column named "RecordTypeA", it is a bit data type with (1 and 0) entries. 1 equals Type A and 0 equals Type B.
What I am trying to do is show the value of the RecordTypeA column for the store if there are entries of 1 and / or 0 on the same date on the same row.
Scenario 1 (One row returns for the store for the date): RecordTypeA column value = '1'
There is one row in the table for the store and date and the RecordTypeA column = '1' :
Scenario 2 (Two Rows return for the store for the same date):
Row One - RecordTypeA = '1'
Row Two - RecordTypeA = '0' (The column is still named RecordTypeA, but value '0' means something different so I want to create a column name?)
Scenario 3 (One row returns for the store for the date):
RecordTypeA column value = '0'
There is one row in the table for the store and date and the RecordTypeA column = '0' :
My issue is that I am getting multiple rows returned when the store has a RecordtypeA = 0 and a RecordtypeA = 1 row. Which I need to return on the same row. (Create columns that hold both 1 and 0 or Null.
What I am getting is
StoreID Date RecordTypeA
1234 2020-01-04 0
1234 2020-01-05 0
1234 2020-01-05 1
Needed:
StoreID Date RecordTypeA RecordTypeB
1234 2020-01-04 0 NULL
1234 2020-01-05 0 1
I have tried adding in case statements but I have not been able to get the one row as needed. Also, searched and tried PIVOT statements (I don't truly understand PIVOTs) but I get an error on the RecordTypeA Bit type.
Case when s.RecordTypeA = '1' Then 'TypeA' Else 'Null' End as Type
Case when s.RecordTypeA = '0' Then 'TypeB' Else 'Null' End as Type
SELECT r.StoreID,
r.CreatedDate,
s.RecordTypeA
From Request r
Inner Join Stores s on r.id = s.id
Group by r.StoreID,
r.CreatedDate,
s.RecordTypeA
Welcome to stack overflow community!
Have you tried to construct 3 queries and use UNION ALL statement?
You can create a query for StoreID, Date, RecordTypeA, TypeB and Typec.
For example:
SELECT CONCAT(r.StoreID, r.CreatedDate, s.RecordTypeA) AS DATA, 'A' AS TYPE
FROM YOURDATABASE.YOURTABLE
WHERE (A CRITERIA FOR TYPE A)
UNION ALL
SELECT CONCAT(r.StoreID, r.CreatedDate, s.RecordTypeA) AS DATA, 'B' AS TYPE
FROM YOURDATABASE.YOURTABLE
WHERE (A CRITERIA FOR TYPE B)
UNION ALL
...
I use the same alias on the example because with union all, all the queries must have the same columns, so you can use a CONCAT to put all your data from the different queries in one column, the column "TYPE" is for the difference the queries at the result.
Even if you not use concat you can return the columns different but all the queries must have the same column count on the select.
With multiple queries, you can define de criteria you want for type A, B, C through Z but have all of them at one result.
Important: Union all statements are somehow heavy for performance, so have it in mind.

Updating a value in one row where is currently null ,where in other row there is data

I'm working to reconsile or AWS bill in our cost reporting tool we're built and I need some guidence on how to execute this type of update in Postgres.
The AWS bill is in the Table 'BillingData' and every row is the cost billing per 'ResourceId' every hour.
For example, we have
ResourceId|BlendedCost|user:Product|UsageStartDate
i-34r8uefg | 0.8763 |<null>|04-01-01 01:00
i-34r8uefg | 0.8763 |AwesomeProductTag|04-01-01 02:00
This shows that at hour 01 the instance was not tagged, but on hour 02 it was. We have 1,000s of rows like this.
What I would like to do is wherever there's a row that has NULL data for column "user:Product", populate that column with data that is elsewhere in that table, for the same "ResourceId".
In more clear terms, someone when they created 'i-34r8uefg' did not tag it properly, but did so later on. I have the following query which gives me rows where instances are not tagged at one hour, but tagged at a different hour
select "ResourceId","user:Product" from billingdata
where "user:Product" NOTNULL
and "ResourceId" in
(select DISTINCT "ResourceId"
from billingdata
where "user:Product" ISNULL);
I want to set the "user:Product" where it is null at one hour(row), to the value that exists later on in the table.
Assuming your requirements are:
Two rows must have the same ResourceId
The target row must have a null user:Product
The source tow must have a non-null user:Product
The source row must be entered after the target row
...then you can use this:
UPDATE "BillingData" AS "Target" SET
"user:Product" = "Source"."user:Product"
FROM "BillingData" AS "Source"
WHERE "Source"."ResourceId" = "Target"."ResourceId"
AND "Target"."user:Product" IS NULL
AND "Source"."user:Product" IS NOT NULL
AND "Target"."UsageStartDate" < "Source"."UsageStartDate"
;
Note that if you have two source rows with the same ResourceId but different non-null user:Product values then it will be a crapshoot as to which row is used as the source for the update. You should check the uniqueness of source rows beforehand using a query like this:
SELECT
"ResourceId"
FROM "BillingData"
WHERE "user:Product" IS NOT NULL
GROUP BY "ResourceId"
HAVING COUNT(*) > 1
...or alternately, use this as a filter predicate in your query to avoid the problem (but not fully solve your original problem), like so:
UPDATE "BillingData" AS "Target" SET
"user:Product" = "Source"."user:Product"
FROM "BillingData" AS "Source"
WHERE "Source"."ResourceId" = "Target"."ResourceId"
AND "Target"."user:Product" IS NULL
AND "Source"."user:Product" IS NOT NULL
AND "Target"."UsageStartDate" < "Source"."UsageStartDate"
AND NOT EXISTS (
SELECT *
FROM "BillingData"
WHERE "user:Product" IS NOT NULL
AND "ResourceId" = "Source"."ResourceId"
HAVING COUNT(*) > 1
)
;