Related
I have the table below. I'm looking for the both the max value in each column AND it's matching username (all values of NULL to be ignored).
A bunch of mad googling has lead me to believe I need to find the max values and then use a second query to find the matching username?
But is there a query that can return this in one go?
ID username Vale Jorge Andrea
-------------------------------------------
01 John 2 6 NULL
02 Ted NULL 0 0
03 Marcy NULL 2 1
Output would be...
John Jorge 6
John Vale 2
Marcy Andrea 1
There's different ways of looking at it, here's a table that gives a row for each username that has a matching max value:
SELECT
username
, IF (max_vale = t.vale, max_vale, NULL) AS for_vale
, IF (max_jorge = t.jorge, max_jorge, NULL) AS for_jorge
, IF (max_andrea = t.andrea, max_andrea, NULL) AS for_andrea
FROM (
SELECT
MAX(vale) AS max_vale
, MAX(jorge) AS max_jorge
, MAX(andrea) AS max_andrea
FROM t
) y
JOIN t ON (
t.vale = max_vale
OR t.jorge = max_jorge
OR t.andrea = max_andrea
)
http://sqlfiddle.com/#!9/58e37d/5
This gives:
username for_vale for_jorge for_andrea
----------------------------------------------
John 2 6 (null)
Marty (null) (null) 1
Basically, all I'm doing is selecting the specific column max values, then using that query as the source for another query that just looks at the MAX generated columns, and filters (IF()) based on the matches found.
This is one reasonably simple way of doing it... make a table of all the maximum values and join it to the usernames on matching the value with the max. Uses the fact that NULL isn't equal to anything. I haven't attempted to order the results but that is easy enough with adding an ORDER BY clause.
select username, name, COALESCE(mv.vale, mv.jorge, mv.Andrea) as value
from table1
join
(select 'Vale' as name, max(vale) as vale, NULL as jorge, NULL as andrea from table1
union ALL
select 'Jorge', NULL, max(jorge), NULL from table1
union all
select 'Andrea', NULL, NULL, max(andrea) from table1) mv
on table1.vale = mv.vale or table1.jorge = mv.jorge or table1.andrea = mv.andrea
Output
username name value
John Vale 2
John Jorge 6
Marcy Andrea 1
To extrapolate this to more columns is reasonably straightforward (if somewhat painful) e.g. to add a column called fred you would use (changes inside **):
select username, name, COALESCE(mv.vale, mv.jorge, mv.Andrea**, mf.fred**) as value
from table1
join
(select 'Vale' as name, max(vale) as vale, NULL as jorge, NULL as andrea**, NULL as fred** from table1
union ALL
select 'Jorge', NULL, max(jorge), NULL**, NULL** from table1
union all
select 'Andrea', NULL, NULL, max(andrea)**, NULL** from table1) mv
**union all
select 'Fred', NULL, NULL, max(fred), NULL from table1) mf**
on table1.vale = mv.vale or table1.jorge = mv.jorge or table1.andrea = mv.andrea **or table1.fred = mf.fred**
If you have access to stored procedures, you can also do it like this (in a much more flexible way in terms of columns)
DROP PROCEDURE IF EXISTS list_maxes;
DELIMITER //
CREATE PROCEDURE list_maxes(tname VARCHAR(20), column_list VARCHAR(1000))
BEGIN
DECLARE maxv INT DEFAULT 0;
DECLARE cpos INT;
DECLARE colname VARCHAR(20);
-- loop through the column names
WHILE (LENGTH(column_list) > 0)
DO
SET cpos = LOCATE(',', column_list);
IF (cpos > 0) THEN
SET colname = LEFT(column_list, cpos - 1);
SET column_list = SUBSTRING(column_list, cpos + 1);
ELSE
SET colname = column_list;
SET column_list = '';
END IF;
-- find the maximum value of this column
SET #getmax = CONCAT('SELECT MAX(', colname, ') INTO #maxv FROM Table1');
PREPARE s1 FROM #getmax;
EXECUTE s1;
DEALLOCATE PREPARE s1;
-- now find the user with the maximum value
SET #finduser = CONCAT("SELECT username, '", colname, "' AS name, ", colname, ' AS value FROM ', tname,' WHERE ', colname, ' = ', #maxv);
PREPARE s2 FROM #finduser;
EXECUTE s2;
DEALLOCATE PREPARE s2;
END WHILE;
END//
DELIMITER ;
CALL list_maxes('table1', 'Vale,Jorge,Andrea')
Output
John Vale 2
John Jorge 6
Marcy Andrea 1
This is quite long but working. I combine all columns into one table using UNION ALL. Then get the max value per surname. Join this to the original table using the surname and value. Order by value in descending order.
select tv.*
from( select surname, max(val) as maxval
from (
select username,'vale' as surname,vale as val
from tbl
union all
select username,'jorge' as surname,jorge
from tbl
union all
select username,'andrea' as surname,andrea
from tbl) tab
group by surname) tt
join (
select username,'vale' as surname,vale as val
from tbl
union all
select username,'jorge' as surname,jorge
from tbl
union all
select username,'andrea' as surname,andrea
from tbl) tv
on tt.surname=tv.surname and tt.maxval=tv.val
order by tv.val desc;
As a whole other way to resolve the request, we may look at the model, e.g., the fact that what appears to be domain content is represented as columns instead of rows. This is typical of a source aggregate query (pivot or rollup to get aggregated totals or groupings, for instance), but if the underlying data spreads out, should perhaps be based on the transactional integrity of that data source (the "spread out" underlying source).
Basically, I wonder why there are Vale, Jorge and Andrea columns at all in the database. This implies it's already been summarized.
So we may look at an alternate model that is notably easier to navigate for these purposes:
CREATE TABLE IF NOT EXISTS `user` (
`id` MEDIUMINT NOT NULL AUTO_INCREMENT,
`username` varchar(20) NOT NULL,
PRIMARY KEY (`id`)
) DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `prospect` (
`id` MEDIUMINT NOT NULL AUTO_INCREMENT,
`name` varchar(20) NOT NULL,
PRIMARY KEY (`id`)
) DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `aggregate` (
`id` MEDIUMINT NOT NULL AUTO_INCREMENT,
`user_id` int(6) unsigned NOT NULL,
`prospect_id` int(6) unsigned NOT NULL,
`total` int(6) unsigned NOT NULL,
PRIMARY KEY (`id`)
) DEFAULT CHARSET=utf8;
SELECT
user.username
, prospect.name
, MAX(aggregate.total) AS max_aggregate
FROM aggregate
JOIN user ON user_id = user.id
JOIN prospect ON prospect_id = prospect.id
GROUP BY username
This produces:
John Andrea 6
Marty Jorge 2
Ted Jorge 5
http://sqlfiddle.com/#!9/07ba0c/1
This may not be useful to you now, but as your experience grows and your experience with advanced querying evolves, this will make more sense. The main difficulty may be that the core data is already turned, making the querying more difficult because what you want is a different dimension than what you may have already derived.
How to pass comma separated value to a stored procedure directly in SQLSERVER?
ALTER PROCEDURE [dbo].[CM_GetUnreadCount_ByUserJid_bilal_onlyfortesting] (#UserID AS VARCHAR(MAX))
AS
DECLARE #myTable TABLE (
MessageId BIGINT,
RecieverId VARCHAR(100)
)
INSERT INTO #myTable (
MessageId,
RecieverId
)
SELECT MAX(MessageID),
ReceiverID
FROM dbo.CM_MessageStatus
WHERE ReceiverID IN (#UserID)
AND DeliveredDate IS NOT NULL
GROUP BY ReceiverID
EXEC [CM_GetUnreadCount_ByUserJid_bilal_onlyfortesting] '123', '456'
Error:
Msg 8144, Level 16, State 2, Procedure
CM_GetUnreadCount_ByUserJid_bilal_onlyfortesting, Line 0 Procedure or
function CM_GetUnreadCount_ByUserJid_bilal_onlyfortesting has too many
arguments specified.
You need to write
exec [CM_GetUnreadCount_ByUserJid_bilal_onlyfortesting] '123,456'
And then you need to string_split it inside your stored procedure, just like this:
Notice - You can only use string_split in 2016 otherwise you need to do something else like XML PATH
CREATE PROCEDURE [dbo].[CM_GetUnreadCount_ByUserJid_bilal_onlyfortesting] ( #UserID AS VARCHAR(MAX))
AS
DECLARE #myTable TABLE
(
MessageId BIGINT,
RecieverId VARCHAR(100)
)
DECLARE #UserTable TABLE
( UserId varchar(MAX)
)
INSERT INTO #UserTable (UserID)
select * from string_split(#UserID,',');
INSERT INTO #myTable
( MessageId, RecieverId )
SELECT MAX(MessageID),RecieverID
FROM dbo.Message_status
WHERE RecieverID IN (select * from #UserTable)
AND DeliveredDate IS NOT NULL
GROUP BY RecieverID
--RETURN OUTPUT TO TEST
Select * from #myTable
LocationID LocationName
50 Sharjah
51 Qatar
55 Rigga
6 Dubai Mall
I need to make sql query that takes locationId as parameter,
if locationId is passed 0 then I want to show all this records otherwise show as per locationId passed.
I tried case statement but not succeeded.
Try this : Here #Parm will be passing value to your script
SELECT *
FROM TableName
WHERE (#Parm = 0 OR LocationID = #Parm)
DECLARE #locationId INT;
SELECT LocationID, LocationName
FROM yourTable
WHERE #locationId = 0 OR LocationID = #locationId
I have created a stored proc for you.
CREATE TABLE #LOCATIONS
(
LocationID INT
,LocationName VARCHAR(100)
)
INSERT INTO #LOCATIONS VALUES(
50,'Sharjah')
,(51,'Qatar')
,(55,'Rigga')
,(6,'Dubai Mall')
SELECT * FROM #LOCATIONS
GO
CREATE PROC LOC ( #LOC_ID INT )
AS
BEGIN
IF(#LOC_ID = 0)
SELECT * FROM #LOCATIONS
ELSE
SELECT * FROM #LOCATIONS WHERE LocationID = #LOC_ID
END
GO
EXEC LOC 0 -- Try Executing the stored proc like this
EXEC LOC 55 -- Try Executing the stored proc like this
I am trying to cast some bad data into proper data using CAST(column AS DECIMAL(7,2)), which works when just SELECTing, but once combined with an INSERT statement, the statement fails:
/* setup */
drop table if exists dst;
create table dst (
id int not null auto_increment
, columnA decimal(7,2)
, primary key (id)
);
drop table if exists src;
create table src (
id int not null auto_increment
, columnA varchar(255)
, primary key (id)
);
insert into src (columnA) values ('');
/* test */
/* This works as I would like it to*/
select CAST(columnA AS decimal(7,2)) from src; /* returns 0.00 */
/* This fails */
insert into dst (columnA)
select CAST(columnA AS decimal(7,2)) from src;
/*0 19 21:01:56 insert into dst (columnA)
select cast(columnA as decimal(7,2)) from src Error Code: 1366. Incorrect decimal value: '' for column '' at row -1 0.000 sec*/
Edit: this is a pared down reproduction example - the data I am
working with is much more varied than just an empty string - it might
also be a non-numeric string value or a numeric value that is outside
of the bounds of decimal(7,2) - in which the CAST statement works
the way I would like it to when only in a SELECT but fails when
trying to use them as part of the INSERT.
Am I doing something wrong, or is there another way to achieve what I am looking for?
I am using MYSQL 5.6.11 on WIN 7 x64
Set explicitly the value for the empty string with CASE or IF:
SELECT CASE WHEN columnA = '' THEN 0 ELSE CAST(columnA AS decimal(7,2)) END
FROM src;
SELECT IF(columnA <> '', CAST(columnA AS decimal(7,2)), 0)
FROM src;
Or use 2 SELECTs with UNION ALL :
SELECT CAST(columnA AS decimal(7,2))
FROM src
WHERE columnA <> ''
UNION ALL
SELECT 0
FROM src
WHERE columnA = '';
You can try using REGEXP to filter out any irregularities in source data:
INSERT INTO dst (columnA)
SELECT CAST(CASE
WHEN columnA REGEXP '^[0-9]{0,7}(\.[0-9]{0,2})$' THEN columnA
ELSE 0
END AS decimal(7,2))
FROM src;
SQL Fiddle Demo here
My input is like
(abcd#123, xyz#324, def#567)
I want an output
col1 Col2
abcd 123
xyz 324
def 567
and so on
Column 1 should have abcd and xyz, and column 2 should have 123 and 324 both in different rows and so on. and the String can be of any size.
Thank you
Try this
SELECT LEFT('abcd#123', CHARINDEX('#', 'abcd#123')-1),
RIGHT('abcd#123', CHARINDEX('#', 'abcd#123')-1)
You will need to use CHARINDEX() and SUBSTRING() functions to split your input values.
your actual problem is turning a complex String into a table i gues.
That for i found help with a procedure about 1 year ago, that does exactly that:
CREATE FUNCTION [dbo].[fnSplitString] (
#myString varchar(500),
#deliminator varchar(10))
RETURNS
#ReturnTable TABLE (
[id] [int] IDENTITY(1,1) NOT NULL,
[part] [varchar](50) NULL
)
AS
BEGIN
Declare #iSpaces int
Declare #part varchar(50)
Select #iSpaces = charindex(#deliminator,#myString,0)
While #iSpaces > 0
BEGIN
Select #part = substring(#myString,0,charindex(#deliminator,#myString,0))
Insert Into #ReturnTable(part)
Select #part
Select #myString = substring(#mystring,charindex(#deliminator,#myString,0)+ len(#deliminator),len(#myString) - charindex(' ',#myString,0))
Select #iSpaces = charindex(#deliminator,#myString,0)
END
If len(#myString) > 0
Insert Into #ReturnTable
Select #myString
RETURN
END
Create this procedure and you can do:
DECLARE #TestString varchar(50)
SET #TestString = 'abcd#123,xyz#324,def#567'
Select * from dbo.fnSplitString(#TestString, ',')
Result:
id| part
1 | abcd#123
2 | xyz#324
3 | def#567
this part you can combine with Leonardos answer:
SELECT
LEFT(part, CHARINDEX('#', part)-1) As Col1,
RIGHT(part, LEN(part) - CHARINDEX('#', part)) As Col2
from dbo.fnSplitString(#TestString, ',')
to get your problem solved.
(little note: the function has little issues with whitespaces, so please try to avoid them there)