rows to column and vice-versa conversion - sql-server-2008

By running the following query I will get the result below. I want to convert this result into rows and whatever is there as row over ther those I want to convert into columns.
I want that project_level and account_level to come in rows and the remaining in rows goes to column.
SELECT S.Description,T.Project_Level,S.Account_Level FROM
(
SELECT DISTINCT SM.Description, COUNT(VA.FK_Status_ID) AS Project_Level,SM.Seq
From DeliveryPlatform_APPS.VA.DP_VA_Item_Status_Master SM
LEFT OUTER JOIN DeliveryPlatform_APPS.VA.DP_VA_Items VA ON SM.Status_ID = VA.FK_Status_ID
AND VA.FK_DP_Entity_Type_ID = 1 AND FK_DP_Entities=671
GROUP BY SM.Description,SM.Seq,FK_Status_ID
) AS T
INNER JOIN
(
SELECT DISTINCT SM.Description, COUNT(VA.FK_Status_ID) AS Account_Level,SM.Seq
From DeliveryPlatform_APPS.VA.DP_VA_Item_Status_Master SM
LEFT OUTER JOIN DeliveryPlatform_APPS.VA.DP_VA_Items VA ON SM.Status_ID = VA.FK_Status_ID
AND VA.FK_DP_Entity_Type_ID = 1 AND FK_DP_Entities=671
GROUP BY SM.Description,SM.Seq,FK_Status_ID
) AS S
ON S.Description=T.Description
--ORDER BY Seq
Description Project_Level Account_Level
Accepted 0 0
Closed 0 0
Delivered 0 0
Dropped 0 0
Open 0 0
Parked 0 0
Shortlisted 0 0
Work In Progress 0 0

You should check SQL Server PIVOT and UNPIVOT techniques to accomplish your intent:
http://msdn.microsoft.com/en-us/library/ms177410(v=sql.105).aspx

Related

Is there way I can SELECT and JOIN COUNT different queries?

I'm using MYSQL and I have a message table like below.
id
parent_id
sender_id
receiver_id
content
readed
sender_deleted
receiver_deleted
created
18
0
6
1
testab
0
0
0
2021-10-28 01:13:42
19
18
6
1
testcd
0
0
0
2021-10-28 01:14:55
I'm trying to combine two queries in one. Selecting and count where readed value is 0.
This query is selecting query.
SELECT * FROM message
WHERE (sender_id = 1 OR receiver_id = 1) AND (id = 18 OR parent_id = 18);
And this query is counting query.
SELECT COUNT(id) FROM message
WHERE (sender_id = 1 OR receiver_id = 1)
AND (id = 18 OR parent_id = 18) AND (readed = 0);
I'm trying to combine these two using LEFT JOIN.
SELECT a.id, a.parent_id, a.content, COUNT(b.id) AS unreaded_message
FROM message a
LEFT JOIN message c ON a.id = c.id AND (readed = 0)
GROUP BY A.ID, A.Date
ORDER BY a.id;
But I'm getting error like below.
ERROR 1054 (42S22): Unknown column 'b.id' in 'field list'
Is there way I can left join count query?
I notice these issues when testing the query:
The incorrect alias of b.id for the COUNT(). That's where the error is indicating.
The non-existing A.Date from table message. Based on your table sample, you don't have a column named as Date. Instead you have a column named created that stores date+time.
The query itself is incompatible with sql_mode=only_full_group_by. Since MySQL v5.7.5, this setting is on by default, with a very good reason of - "if the only_full_group_by mode is turned off the server is free to choose any value from each group, so unless they are the same, the values chosen are nondeterministic, which is probably not what you want (refer: "MySQL Handling of Group By" docs.
Now, if I'm not mistaken, content stores the text messages, correct? And since id=19 parent is id=18, it should be in the same row, with the total count of unread message is 2. I'm not sure if that's what you're really looking for but I'll post two queries; one is to address your current issue and another to suggest that it maybe what you're looking for.
For your current issue, you can achieve the desired result without self-join, and instead of using COUNT(), you can use SUM() with CASE expression like below:
SELECT id, parent_id, content,
SUM(CASE WHEN readed=0 THEN 1 ELSE 0 END) AS unreaded_message
FROM message
GROUP BY id, parent_id, content;
COUNT() will take every row in the query result depending on your conditions. The replacement of SUM() with CASE expression here is just telling the query to:
SUM(CASE WHEN readed=0 THEN 1 ELSE 0 END) AS unreaded_message
-- if the readed column is 0 (zero) then give it 1, else give it 0 then add them up.
The second suggestion I have is this, let's assume you have more rows in the table like this:
id
parent_id
sender_id
receiver_id
content
readed
sender_deleted
receiver_deleted
created
18
0
6
1
testab
1
0
0
2021-10-28 01:13:42
19
18
6
1
testcd
1
0
0
2021-10-28 01:14:55
20
18
6
1
testde
0
0
0
2021-10-28 01:15:05
21
0
6
1
testfg
0
0
0
2021-10-28 02:34:11
22
21
6
1
testhi
0
0
0
2021-10-28 02:44:01
With id of 18,19 both have readed=1, the query you have and the one I suggested above will return result like this:
id
parent_id
content
unreaded_message
18
0
testab
0
19
18
testcd
0
20
18
testde
1
21
0
testfg
1
22
21
testhi
1
But I have a feeling that you probably want something like this:
m_id
contents
unreaded_message
18
testde
1
21
testgftesthi
2
If that's a possibility, then you can just run this query:
SELECT CASE WHEN parent_id=0 THEN id ELSE parent_id END AS m_id,
GROUP_CONCAT(CASE WHEN readed=0
THEN content ELSE '' END
ORDER BY readed, id SEPARATOR '\r\n' ) AS contents,
SUM(CASE WHEN readed=0 THEN 1 ELSE 0 END) AS unreaded_message
FROM message
GROUP BY m_id;
Demo fiddle
You are using in the query B alias in the "COUNT" satement. You need to use A or C alias like these example:
SELECT a.id, a.parent_id, a.content, COUNT(c.id) AS unreaded_message
FROM message a
LEFT JOIN message c ON a.id = c.id AND (readed = 0)
GROUP BY A.ID, A.Date
ORDER BY a.id;

MySQL query to get item counts per month - displayed in row for each item

I have MySQL table like this (there will be more items in real scenario):
ID
item
date
1
a
2021-04-01
2
a
2021-04-05
3
a
2021-05-07
4
b
2021-07-09
And I want to create a table (the result will be displayed in HTML table) where each unique item name will have one row and also counts of the item for every month of whole year. So the result for the case above will look like:
item
m1
m2
m3
m4
m5
m6
m7
m8
m9
m10
m11
m12
a
0
0
0
2
1
0
0
0
0
0
0
0
b
0
0
0
0
0
0
1
0
0
0
0
0
I would be able to do that by 2 separate SELECT queries where the second one would probably be in the for loop, but I can't figure out the way how to do that in one SELECT or one multiselect query, if that is even possible.
I got only that far, then got stucked:
SELECT MONTH(date) as m, COUNT(item) as c
FROM itemtable
WHERE YEAR(date)=YEAR(CURRENT_DATE()) AND item="a"
GROUP BY MONTH(date)
It's a pivot operation. In MySQL I usually do it via conditional aggregation:
SELECT item,
SUM(CASE WHEN MONTH(date) = 1 THEN 1 ELSE 0 END) as m1,
SUM(CASE WHEN MONTH(date) = 2 THEN 1 ELSE 0 END) as m2,
...
SUM(CASE WHEN MONTH(date) = 12 THEN 1 ELSE 0 END) as m12
FROM itemtable
WHERE date BETWEEN '2021-01-01' and '2021-12-31'
GROUP BY item
To see how it works, remove the SUM/GROUP BY and look at the raw CASE WHEN - it splits the data across 12 columns in a diagonal, and then the sum/group squishes it to one row:
A A A B C
B -> B ->
C C
I agree with strawberry's observation that this is often best handled in the app code rather than the DB, but as the data is fixed at "month 1" thru "month 12" it does make it relatively easier to do in the DB

Count Consecutive Numeric values in SQL

There are already many existing questions about this scenario, however I am unable to replicate the answers in my scenario.
I have a following sample Data Set:
ID Number | Values
754321 0
754321 0
754321 0
754321 0
754321 1
754321 0
754321 1
754321 0
754321 2
754321 0
754329 3
754329 4
754329 5
754329 6
754329 7
754329 8
754329 9
I want the SQL query that outputs the ID Number with the number of times the value of "0" appears consecutively. So, for the above table I would like to get the output as follows:
ID Number Count of Consecutive 0 Values
754321 4
This is a form of gaps-and-islands problem. You can assign each 0 a group by counting the number of non-zero values before it. Then aggregate.
However, SQL tables represent unordered sets. There is no ordering unless a column specifies the ordering. Let me assume you have one. Then:
select count(*)
from (select t.*,
sum(values <> 0) over (partition by idnumber order by <ordering col>) as grp
from t
) t
where values = 0
group by idnumber, grp;
If you're running a version of MySQL that doesn't support window functions, you can implement this functionality using variables:
SELECT `ID Number`, MAX(cnt) AS `Max Consecutive 0 Values`
FROM (SELECT `ID Number`, SUM(`Values` = 0) AS cnt
FROM (SELECT `ID Number`, `Values`,
#cnz:= CASE WHEN `Values` != 0 THEN #cnz + 1
ELSE #cnz
END AS cnz
FROM data
CROSS JOIN (SELECT #cnz := 0) init
ORDER BY date
) c
GROUP BY `ID Number`, cnz) s
GROUP BY `ID Number`
Output
ID Number Max Consecutive 0 Values
754321 4
754329 0
Demo on SQLFiddle

How to combine WHERE and HAVING in a MySQL query

How do I write a query for MySQL that would filter out where total pay is less than zero.
I have tried different commands using 'HAVING' but I can't figure out the proper syntax.
This is my command:
SELECT tickets.idno, tickets.PAY, tickets.MATERIAL
FROM tickets
WHERE ( tickets.PAY <> 0 OR tickets.MATERIAL <> 0 ) AND tickets.DISTRICT = 'HO' AND tickets.DATEPAID IS NULL
ORDER BY tickets.NAME
with this result set:
HO0045 -140 0
HO2203 -45 0
HO2411 -5 0
HO2411 20 0
HO3448 -156 0
HO2519 2000 0
HO0075 -300 0
HO1669 -55 0
HO2666 -200 0
HO2666 -200 0
HO3447 -400 0
HO3447 400 0
This is result I get now, but it needs to eliminate those records where the total pay + total material for all rows for a given IDNO are less than zero. For example; all the rows for HO2411 and HO2519 should appear but not the rest.
HAVING is meant to filter out whole groups, but to do that, you need to reduce the query with GROUP BY. But unfortunately you can't reverse the grouping operation after filtering groups.
So you have to do a subquery to filter the groups, then JOIN that result back to the table to find the rows with the corresponding idno:
SELECT t.idno, t.PAY, t.MATERIAL
FROM tickets AS t
INNER JOIN (
SELECT idno, SUM(PAY) AS PAY, SUM(MATERIAL) AS MATERIAL
FROM tickets
GROUP BY idno
HAVING PAY + MATERIAL < 0
) AS total USING (idno)
WHERE t.DISTRICT = 'HO' AND t.DATEPAID IS NULL
ORDER BY t.NAME;

Count numbers without Zero

I have a query who works correctly, but where I want to make a change.
The query counts all notes for id_service = 89.
The notes are numbers between 0 and 3 not more.
My query counts all those notes to get an Average.
All works fine.
The change what I want is, that he counts only the notes between 1 and 3 (not 0).
I don't know how I can do that.
Example:
This query count how much time I get the number 3 with all conditions.
Here the query:
SELECT round( avg( AVG =3 ) * count( AVG ) ) AS New
, sma_famille.famille
FROM (
SELECT ROUND( SUM( note ) / count( note ) ) AS AVG
, sma_famille.famille
, sma_agents.nom
FROM sma_notes
INNER JOIN sma_famille
ON sma_famille.id_service =89
INNER JOIN sma_agents
ON sma_notes.id_agent = sma_agents.id_agent
INNER JOIN sma_service_activite
ON sma_service_activite.id_activite = sma_notes.id_activite
AND sma_service_activite.id_famille = sma_famille.id_famille
AND sma_service_activite.id_service = sma_famille.id_service
GROUP BY sma_famille.famille, sma_agents.nom
) AS FN
LEFT JOIN sma_famille
ON sma_famille.id_service =89
AND FN.famille = sma_famille.famille
GROUP BY FN.famille
An example:
In Bio I can give 2 notes per persons like Bio Part1 and Bio Part2.
In my example I have two persons.
I give in Bio Part1 the note "3" for both and in Bio Part2 i don't give a note, so that there are the note "0"!
Here the result of my query:
That is what I get:
Note Math English Bio
1 0 0 0
2 0 0 2
3 0 0 0
That is what I want:
Note Math English Bio
1 0 0 0
2 0 0 0
3 0 0 2
Without the note "0" I must get an average of 100% for the note "3".
Now i get:
Note Math English Bio
1 0
2 0
3 2
And not
Note Math English Bio
1 0 0 0
2 0 0 0
3 0 0 2
How can i get the "0" in the resultset
Anybody an idea?
One way to get a count of values > 0 would be:
count(case when note > 0 then note end)
(Where note is 0, the case evaluates as null which is not counted.)
Although the simplest way might be
sum(sign(note))