How to pivot table in sql? - sql

I need to pivot a table, but I am stuck because of reapeated Action values.
Goal: extract values from the Action column and use them as the headers for new columns. Then, fill the new table with values from the Val column. In this instance, there is only one group, so you can utilize a window function to capture all groups with ID column. All SN are unique, but other actions can be repeated for the same SN
I have a table:
Val
Action
ID
SN1844Q
SN
94a52150-a24f-11ed
2000
Check_X
94a52150-a24f-11ed
1
Pass
94a52150-a24f-11ed
2022-01-12 23:51:31
DateTime
94a52150-a24f-11ed
up
Position
94a52150-a24f-11ed
back
Position
94a52150-a24f-11ed
890
Check_X
94a52150-a24f-11ed
SN1845Q
SN
28497a86-8e8e-44da
...
...
...
I want to see:
SN
Check_X
Pass
DateTime
Position
SN1844Q
2000
1
2022-01-12 23:51:31
up
SN1844Q
890
1
2022-01-12 23:51:31
back
...
...
...
...
...

You have to use SQL pivot with a dynamic query click here .

SELECT SN,
MAX(CASE WHEN Action = 'Action 1' THEN Val END) AS "Action 1",
MAX(CASE WHEN Action = 'Action 2' THEN Val END) AS "Action 2",
MAX(CASE WHEN Action = 'Action 3' THEN Val END) AS "Action 3"
FROM original_table
GROUP BY SN
In this query, the MAX function is used in the CASE statement to aggregate the values from the Val column, while the GROUP BY clause is used to group the results by the SN column. The CASE statement is used to match the values in the Action column and return the corresponding values from the Val column. The result of the query will be a new table with columns for each unique value in the Action column, with the values from the Val column filling in the appropriate cells

Related

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.

SQL Rows to Columns if column values are unknown

I have a table that has demographic information about a set of users which looks like this:
User_id Category IsMember
1 College 1
1 Married 0
1 Employed 1
1 Has_Kids 1
2 College 0
2 Married 1
2 Employed 1
3 College 0
3 Employed 0
The result set I want is a table that looks like this:
User_Id|College|Married|Employed|Has_Kids
1 1 0 1 1
2 0 1 1 0
3 0 0 0 0
In other words, the table indicates the presence or absence of a category for each user. Sometimes the user will have a category where the value if false, sometimes the user will have no row for a category, in which case IsMember is assumed to be false.
Also, from time to time additional categories will be added to the data set, and I'm wondering if its possible to do this query without knowing up front all the possible category names, in other words, I won't be able to specify all the column names I want to count in the result. (Note only user 1 has category "has_kids" and user 3 is missing a row for category "married"
(using Postgres)
Thanks.
You can use jsonb funcions.
with titles as (
select jsonb_object_agg(Category, Category) as titles,
jsonb_object_agg(Category, -1) as defaults
from demog
),
the_rows as (
select null::bigint as id, titles as data
from titles
union
select User_id, defaults || jsonb_object_agg(Category, IsMember)
from demog, titles
group by User_id, defaults
)
select id, string_agg(value, '|' order by key)
from (
select id, key, value
from the_rows, jsonb_each_text(data)
) x
group by id
order by id nulls first
You can see a running example in http://rextester.com/QEGT70842
You can replace -1 with 0 for the default value and '|' with ',' for the separator.
You can install tablefunc module and use the crosstab function.
https://www.postgresql.org/docs/9.1/static/tablefunc.html
I found a Postgres function script called colpivot here which does the trick. Ran the script to create the function, then created the table in one statement:
select colpivot ('_pivoted', 'select * from user_categories', array['user_id'],
array ['category'], '#.is_member', null);

Find certain values and show corresponding value from different field in SQL

So I found these 2 articles but they don't quite answer my question...
Find max value and show corresponding value from different field in SQL server
Find max value and show corresponding value from different field in MS Access
I have a table like this...
ID Type Date
1 Initial 1/5/15
1 Periodic 3/5/15
2 Initial 2/5/15
3 Initial 1/10/15
3 Periodic 3/6/15
4
5 Initial 3/8/15
I need to get all of the ID numbers that are "Periodic" or NULL and corresponding date. So I want a to get query results that looks like this...
ID Type Date
1 Periodic 3/5/15
3 Periodic 3/6/15
4
I've tried
select id, type, date1
from Table1 as t
where type in (select type
from Table1 as t2
where ((t2.type) Is Null) or "" or ("periodic"));
But this doesn't work... From what I've read about NULL you can't compare null values...
Why in SQL NULL can't match with NULL?
So I tried
SELECT id, type, date1
FROM Table1 AS t
WHERE type in (select type
from Table1 as t2
where ((t.Type)<>"Initial"));
But this doesn't give me the ID of 4...
Any suggestions?
Unless I'm missing something, you just want:
select id, type, date1
from Table1 as t
where (t.type Is Null) or (t.type = "") or (t.type = "periodic");
The or applies to boolean expressions, not to values being compared.

The MIN() Function Ms Access

this is a sample sql query that i created ms access query. i am trying to get only one row the min(DATE). how ever when i run my query i get multiple lines. any hits? thanks
SELECT tblWarehouseItem.whiItemName,
tblWarehouseItem.whiQty,
tblWarehouseItem.whiPrice,
Min(tblWarehouseItem.whiDateIn) AS MinOfwhiDateIn,
tblWarehouseItem.whiExpiryDate,
tblWarehouseItem.whiwrhID
FROM tblWarehouseItem
GROUP BY tblWarehouseItem.whiDateIn,
tblWarehouseItem.whiItemName,
tblWarehouseItem.whiQty,
tblWarehouseItem.whiPrice,
tblWarehouseItem.whiExpiryDate,
tblWarehouseItem.whiwrhID;
If i have my sql code like that is working as it should:
SELECT MIN(tblWarehouseItem.whiDateIn) FROM tblWarehouseItem;
In the first query, you group by a number of columns. That means the minimum value will be calculated for each group, which in turn means you may have multiple rows. On the other hand, the second query will only get the minimum value for the specified column from all rows, so that there is only one row in the result set.
A simple example is shown below to illustrate the above.
Table:
Key Value
1 1
1 2
2 3
2 4
On Group By Key:
GroupKey MinValue
1 = min(1,2) = 1 -> Row 1
2 = min(3,4) = 3 -> Row 2
On Min (Value)
MinValue
=min(1,2,3,4) = 1 -> Row 1
For a table like above, if you want to select all rows and also show the minimum value from whole table rather than per group, you can do something like this:
select key, (select min(value) from table)
from table
SELECT WI.*
FROM tblWarehouseItem AS WI INNER JOIN (SELECT whiimtID, MIN(tblWarehouseItem.whiDateIn) AS whiDateIn
FROM tblWarehouseItem
GROUP BY whiimtID) AS MinWI ON (WI.whiDateIn = MinWI.whiDateIn) AND (WI.whiimtID = MinWI.whiimtID);

Modifying column value according to status, and adding extra rows. (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.