Set a Value as a Column Name in SQL Select - mysql

Assuming I have a list of values after a select from a table:
DEPARTMENT(Table):
+-----------+---------+-----------+-----------+
| DEPT_NAME | DEPT_ID | DEPT_CITY | DEPT_TYPE |
+-----------+---------+-----------+-----------+
| | | | |
+-----------+---------+-----------+-----------+
Now,
my SELECT Query should be:
SELECT DEPT_NAME,DEPT_ID,DEPT_CITY from LOCATIONS table where (external condition);

i think this can be done using pivot table.
please check this this tutorial http://www.artfulsoftware.com/infotree/queries.php#78
further you can check these in SO itself,
MySQL pivot table

if you're using MS SQL you can use the FOR XML Path('') statement and create dymanic sql query as followed:(I dnn't have access to other SQL Management Systems so I don't know if this would work for them.
DECLARE #columns varchar(255) = (select ',' + [Column] FROM (Values('DEPT_NAME'),('DEPT_ID'),('DEPT_CITY'),('DEPT_TYPE')) as tColumns([column])FOR XML PATH(''))
--select #columns
DECLARE #stmt varchar(255) = 'Select 1 as DummyColumn ' + #columns + ' from LOCATIONS table where (external condition);'
--select #stmt
EXEC(#stmt)
in this case I'm using 255 as the length of the variables; but you can change that to anything of your liking.
(Values('DEPT_NAME'),('DEPT_ID'),('DEPT_CITY'),('DEPT_TYPE')) as tColumns([column]) is an inline values table; you need to replace this with the correct select statement.

Related

Pivoting on temptable data using sql server [duplicate]

I have read the stuff on MS pivot tables and I am still having problems getting this correct.
I have a temp table that is being created, we will say that column 1 is a Store number, and column 2 is a week number and lastly column 3 is a total of some type. Also the Week numbers are dynamic, the store numbers are static.
Store Week xCount
------- ---- ------
102 1 96
101 1 138
105 1 37
109 1 59
101 2 282
102 2 212
105 2 78
109 2 97
105 3 60
102 3 123
101 3 220
109 3 87
I would like it to come out as a pivot table, like this:
Store 1 2 3 4 5 6....
-----
101 138 282 220
102 96 212 123
105 37
109
Store numbers down the side and weeks across the top.
If you are using SQL Server 2005+, then you can use the PIVOT function to transform the data from rows into columns.
It sounds like you will need to use dynamic sql if the weeks are unknown but it is easier to see the correct code using a hard-coded version initially.
First up, here are some quick table definitions and data for use:
CREATE TABLE yt
(
[Store] int,
[Week] int,
[xCount] int
);
INSERT INTO yt
(
[Store],
[Week], [xCount]
)
VALUES
(102, 1, 96),
(101, 1, 138),
(105, 1, 37),
(109, 1, 59),
(101, 2, 282),
(102, 2, 212),
(105, 2, 78),
(109, 2, 97),
(105, 3, 60),
(102, 3, 123),
(101, 3, 220),
(109, 3, 87);
If your values are known, then you will hard-code the query:
select *
from
(
select store, week, xCount
from yt
) src
pivot
(
sum(xcount)
for week in ([1], [2], [3])
) piv;
See SQL Demo
Then if you need to generate the week number dynamically, your code will be:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(Week)
from yt
group by Week
order by Week
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT store,' + #cols + ' from
(
select store, week, xCount
from yt
) x
pivot
(
sum(xCount)
for week in (' + #cols + ')
) p '
execute(#query);
See SQL Demo.
The dynamic version, generates the list of week numbers that should be converted to columns. Both give the same result:
| STORE | 1 | 2 | 3 |
---------------------------
| 101 | 138 | 282 | 220 |
| 102 | 96 | 212 | 123 |
| 105 | 37 | 78 | 60 |
| 109 | 59 | 97 | 87 |
This is for dynamic # of weeks.
Full example here:SQL Dynamic Pivot
DECLARE #DynamicPivotQuery AS NVARCHAR(MAX)
DECLARE #ColumnName AS NVARCHAR(MAX)
--Get distinct values of the PIVOT Column
SELECT #ColumnName= ISNULL(#ColumnName + ',','') + QUOTENAME(Week)
FROM (SELECT DISTINCT Week FROM #StoreSales) AS Weeks
--Prepare the PIVOT query using the dynamic
SET #DynamicPivotQuery =
N'SELECT Store, ' + #ColumnName + '
FROM #StoreSales
PIVOT(SUM(xCount)
FOR Week IN (' + #ColumnName + ')) AS PVTTable'
--Execute the Dynamic Pivot Query
EXEC sp_executesql #DynamicPivotQuery
I've achieved the same thing before by using subqueries. So if your original table was called StoreCountsByWeek, and you had a separate table that listed the Store IDs, then it would look like this:
SELECT StoreID,
Week1=(SELECT ISNULL(SUM(xCount),0) FROM StoreCountsByWeek WHERE StoreCountsByWeek.StoreID=Store.StoreID AND Week=1),
Week2=(SELECT ISNULL(SUM(xCount),0) FROM StoreCountsByWeek WHERE StoreCountsByWeek.StoreID=Store.StoreID AND Week=2),
Week3=(SELECT ISNULL(SUM(xCount),0) FROM StoreCountsByWeek WHERE StoreCountsByWeek.StoreID=Store.StoreID AND Week=3)
FROM Store
ORDER BY StoreID
One advantage to this method is that the syntax is more clear and it makes it easier to join to other tables to pull other fields into the results too.
My anecdotal results are that running this query over a couple of thousand rows completed in less than one second, and I actually had 7 subqueries. But as noted in the comments, it is more computationally expensive to do it this way, so be careful about using this method if you expect it to run on large amounts of data .
This is what you can do:
SELECT *
FROM yourTable
PIVOT (MAX(xCount)
FOR Week in ([1],[2],[3],[4],[5],[6],[7])) AS pvt
DEMO
I'm writing an sp that could be useful for this purpose, basically this sp pivot any table and return a new table pivoted or return just the set of data, this is the way to execute it:
Exec dbo.rs_pivot_table #schema=dbo,#table=table_name,#column=column_to_pivot,#agg='sum([column_to_agg]),avg([another_column_to_agg]),',
#sel_cols='column_to_select1,column_to_select2,column_to_select1',#new_table=returned_table_pivoted;
please note that in the parameter #agg the column names must be with '[' and the parameter must end with a comma ','
SP
Create Procedure [dbo].[rs_pivot_table]
#schema sysname=dbo,
#table sysname,
#column sysname,
#agg nvarchar(max),
#sel_cols varchar(max),
#new_table sysname,
#add_to_col_name sysname=null
As
--Exec dbo.rs_pivot_table dbo,##TEMPORAL1,tip_liq,'sum([val_liq]),sum([can_liq]),','cod_emp,cod_con,tip_liq',##TEMPORAL1PVT,'hola';
Begin
Declare #query varchar(max)='';
Declare #aggDet varchar(100);
Declare #opp_agg varchar(5);
Declare #col_agg varchar(100);
Declare #pivot_col sysname;
Declare #query_col_pvt varchar(max)='';
Declare #full_query_pivot varchar(max)='';
Declare #ind_tmpTbl int; --Indicador de tabla temporal 1=tabla temporal global 0=Tabla fisica
Create Table #pvt_column(
pivot_col varchar(100)
);
Declare #column_agg table(
opp_agg varchar(5),
col_agg varchar(100)
);
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(#table) AND type in (N'U'))
Set #ind_tmpTbl=0;
ELSE IF OBJECT_ID('tempdb..'+ltrim(rtrim(#table))) IS NOT NULL
Set #ind_tmpTbl=1;
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(#new_table) AND type in (N'U')) OR
OBJECT_ID('tempdb..'+ltrim(rtrim(#new_table))) IS NOT NULL
Begin
Set #query='DROP TABLE '+#new_table+'';
Exec (#query);
End;
Select #query='Select distinct '+#column+' From '+(case when #ind_tmpTbl=1 then 'tempdb.' else '' end)+#schema+'.'+#table+' where '+#column+' is not null;';
Print #query;
Insert into #pvt_column(pivot_col)
Exec (#query)
While charindex(',',#agg,1)>0
Begin
Select #aggDet=Substring(#agg,1,charindex(',',#agg,1)-1);
Insert Into #column_agg(opp_agg,col_agg)
Values(substring(#aggDet,1,charindex('(',#aggDet,1)-1),ltrim(rtrim(replace(substring(#aggDet,charindex('[',#aggDet,1),charindex(']',#aggDet,1)-4),')',''))));
Set #agg=Substring(#agg,charindex(',',#agg,1)+1,len(#agg))
End
Declare cur_agg cursor read_only forward_only local static for
Select
opp_agg,col_agg
from #column_agg;
Open cur_agg;
Fetch Next From cur_agg
Into #opp_agg,#col_agg;
While ##fetch_status=0
Begin
Declare cur_col cursor read_only forward_only local static for
Select
pivot_col
From #pvt_column;
Open cur_col;
Fetch Next From cur_col
Into #pivot_col;
While ##fetch_status=0
Begin
Select #query_col_pvt='isnull('+#opp_agg+'(case when '+#column+'='+quotename(#pivot_col,char(39))+' then '+#col_agg+
' else null end),0) as ['+lower(Replace(Replace(#opp_agg+'_'+convert(varchar(100),#pivot_col)+'_'+replace(replace(#col_agg,'[',''),']',''),' ',''),'&',''))+
(case when #add_to_col_name is null then space(0) else '_'+isnull(ltrim(rtrim(#add_to_col_name)),'') end)+']'
print #query_col_pvt
Select #full_query_pivot=#full_query_pivot+#query_col_pvt+', '
--print #full_query_pivot
Fetch Next From cur_col
Into #pivot_col;
End
Close cur_col;
Deallocate cur_col;
Fetch Next From cur_agg
Into #opp_agg,#col_agg;
End
Close cur_agg;
Deallocate cur_agg;
Select #full_query_pivot=substring(#full_query_pivot,1,len(#full_query_pivot)-1);
Select #query='Select '+#sel_cols+','+#full_query_pivot+' into '+#new_table+' From '+(case when #ind_tmpTbl=1 then 'tempdb.' else '' end)+
#schema+'.'+#table+' Group by '+#sel_cols+';';
print #query;
Exec (#query);
End;
GO
This is an example of execution:
Exec dbo.rs_pivot_table #schema=dbo,#table=##TEMPORAL1,#column=tip_liq,#agg='sum([val_liq]),avg([can_liq]),',#sel_cols='cod_emp,cod_con,tip_liq',#new_table=##TEMPORAL1PVT;
then Select * From ##TEMPORAL1PVT would return:
Here is a revision of #Tayrn answer above that might help you understand pivoting a little easier:
This may not be the best way to do this, but this is what helped me wrap my head around how to pivot tables.
ID = rows you want to pivot
MY_KEY = the column you are selecting from your original table that contains the column names you want to pivot.
VAL = the value you want returning under each column.
MAX(VAL) => Can be replaced with other aggregiate functions. SUM(VAL), MIN(VAL), ETC...
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(MY_KEY)
from yt
group by MY_KEY
order by MY_KEY ASC
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT ID,' + #cols + ' from
(
select ID, MY_KEY, VAL
from yt
) x
pivot
(
sum(VAL)
for MY_KEY in (' + #cols + ')
) p '
execute(#query);
select * from (select name, ID from Empoyee) Visits
pivot(sum(ID) for name
in ([Emp1],
[Emp2],
[Emp3]
) ) as pivottable;
Just give you some idea how other databases solve this problem. DolphinDB also has built-in support for pivoting and the sql looks much more intuitive and neat. It is as simple as specifying the key column (Store), pivoting column (Week), and the calculated metric (sum(xCount)).
//prepare a 10-million-row table
n=10000000
t=table(rand(100, n) + 1 as Store, rand(54, n) + 1 as Week, rand(100, n) + 1 as xCount)
//use pivot clause to generate a pivoted table pivot_t
pivot_t = select sum(xCount) from t pivot by Store, Week
DolphinDB is a columnar high performance database. The calculation in the demo costs as low as 546 ms on a dell xps laptop (i7 cpu). To get more details, please refer to online DolphinDB manual https://www.dolphindb.com/help/index.html?pivotby.html
Pivot is one of the SQL operator which is used to turn the unique data from one column into multiple column in the output. This is also mean by transforming the rows into columns (rotating table). Let us consider this table,
If I want to filter this data based on the types of product (Speaker, Glass, Headset) by each customer, then use Pivot operator.
Select CustmerName, Speaker, Glass, Headset
from TblCustomer
Pivot
(
Sum(Price) for Product in ([Speaker],[Glass],[Headset])
) as PivotTable

MySQL sort by case when column value equal search value in where clause dynamic query

I have Table people like this
------------------------------------------------
| id | full_name | first_name |
------------------------------------------------
| 1 | aaa bbb | aaa |
| 2 | bbb ccc | bbb |
| 3 | ddd aaa | ddd |
| 4 | aaa fff | aaa |
| 5 | aaa | aaa |
------------------------------------------------
I try to search for aaa and then sort the result based on first name = where clause like this
SELECT `id`,`full_name`,`first_name` FROM `people`
WHERE `full_name` like '%aaa%'
Order by case
when full_name = 'aaa' then 1
when first_name like '%aaa' then 2
else 3 end asc
so far so good for me what I want is not to set full_name = 'aaa' and first_name like '%aaa' in the order by case manually I want to tell MySQL something like this
Order by case
when full_name = "what I searched for in my full_name where clause" then 1
when first_name like '%what I searched for in my full_name where clause' then 2
else 3 end asc
--
-- Table structure for table `people`
--
CREATE TABLE `people` (
`id` int(10) UNSIGNED NOT NULL,
`full_name` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,
`first_name` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC;
--
-- Dumping data for table `people`
--
INSERT INTO `people` (`id`, `full_name`, `first_name`) VALUES
(1, 'aaa bbb', 'aaa'),
(2, 'bbb ccc', 'bbb'),
(3, 'ddd aaa', 'ddd'),
(4, 'aaa fff', 'aaa'),
(5, 'aaa', 'aaa');
Edit
assuming that I can only write aaa in the query part WHERE full_name like '%aaa%' is there any way to read it from the where clause without a procedure or setting variable to pass it to the order by case
You should look at user-defined variables and then use % as an escape character to check if your input string contains % sign. Based on that, you can change what column you ORDER BY.
I present an example below.
SET #input_str1:='%abc%';
SET #input_str2:='abc';
SELECT
#input_str1,
#input_str2,
CASE WHEN #input_str1 LIKE '%|%%' escape '|' THEN 1 ELSE 0 END order_by_1,
CASE WHEN #input_str2 LIKE '%|%%' escape '|' THEN 1 ELSE 0 END order_by_2
;
I have defined two strings input_str1 that contains % and input_str2 that does not contain the % symbol. Then, in my select query, I select:
input_str1 as first column
input_str2 as the second column
the third column contains 1 if the input_str1 contains % otherwise 0
the fourth column contains 1 if the input_str2 contains % otherwise 0
Therefore, the output is:
%abc% abc 1 0
If you want to order by based on which matches you find (which isn't entirely clear, but I suspect is what you're trying to do), then you can similarly create multiple flags and use those to decide what to order by.
Alternatively, if you just want to determine the order based on what your input string is, using the concept I showed above, you can create another user-defined variable order_by_column based on whether your input string contains % symbol or not.
SET #order_by_column = CASE WHEN #input_str LIKE '%|%%' escape '|' THEN 'first_name' ELSE 'full_name' END;
.. and then use the determined #order_by_column to order your results.
My interpretation is you don't want to change the aaa part three times if need change the search parameter.
create a procedure and use a dynamic query.
create procedure sort(search varchar(15))
begin
set #s :=
concat( 'SELECT `id`,`full_name`,`first_name` FROM `people`',
'WHERE `full_name` like \'%', search, '%\' ',
'Order by case ',
'when full_name = \'', search, '\' then 1 '
'when first_name like \'%' , search, '\' then 2'
'else 3 end asc');
prepare query from #s;
execute query;
deallocate prepare query;
end$$

Substring, Count, and Concat in a Before Insert Trigger?

I have a dilemma where I need to have a MySQL database create a customer number based on the first 3 characters of a new client name entered and to append an incremenenting value. For example:
Table clients:
+----+----------------------------------------------+--------------+
| id | clientName | clientNumber |
+----+----------------------------------------------+--------------+
| 1 | Accelerated Learning | ACC00 |
| 2 | Acceleration Systems | ACC01 |
| 3 | Acme,Inc. | ACM00 |
Normally I would do this in the submission form on the front end, but for reasons unexplained, the spec wants this handled by the DB. I'm trying to write something like this, but can't get it right:
Trigger:
CREATE DEFINER = CURRENT_USER TRIGGER `crmdev`.`clients_BEFORE_INSERT` BEFORE INSERT ON `clients` FOR EACH ROW
BEGIN
DECLARE prefix varchar(3);
DECLARE suffix INT(3);
SET prefix = SUBSTRING(NEW.clientName,1,3);
SET suffix = (
SELECT COUNT(*),SUBSTRING(clientName,1,3) = prefix + 1;
SET NEW.clientNumber = CONCAT(prefix,suffix);
END
Thanks in advance for help/guidance!
You have syntax errors in the suffix assignment. You're missing the FROM and WHERE clauses, and you need to add 1 to the count, not to prefix.
SET suffix = (
SELECT COUNT(*)+1
FROM clients
WHERE SUBSTRING(clientName,1,3) = prefix);
Here's the finished product if helpful to others & thanks to Barmar:
Trigger:
DECLARE prefix varchar(3);
DECLARE suffix INT(3);
SET prefix = SUBSTRING(NEW.clientName,1,3);
SET suffix = (
SELECT COUNT(*)+1
FROM clients
WHERE SUBSTRING(clientName,1,3) = prefix);
SET NEW.clientNumber = UPPER(CONCAT(prefix,LPAD(suffix,3,0)));

How to copy data from one table to another "EXCEPT" one field

How to INSERT into another table except specific field
e.g
TABLE A
ID(auto_inc) CODE NAME
1 001 TEST1
2 002 TEST2
I want to insert CODE and NAME to another table, in this case TABLE B but except ID because it is auto increment
Note: I don't want to use "INSERT INTO TABLE B SELECT CODE, NAME FROM TABLE A", because I have an existing table with around 50 fields and I don't want to write it one by one
Thanks for any suggests and replies
This can't be done without specifying the columns (excludes the primary key).
This question might help you. Copy data into another table
You can get all the columns using information_schema.columns:
select group_concat(column_name separator ', ')
from information_schema.columns c
where table_name = 'tableA' and
column_name <> 'id';
This gives you the list. Then past the list into your code. You can also use a prepared statement for this, but a prepared statement might be overkill.
If this is a one time thing?
If yes, do the insert into tableA (select * from table B)
then Alter the table to drop the column that your dont need.
I tried to copy from a table to another one with one extra field.
source table is TERRITORY_t
* the principle is to create a temp table identical to the source table, adjust column fields of the temp table and copy the content of the temp table to the destination table.
This is what I did:
create a temp table called TERRITORY_temp
generate SQL by running export
CREATE TABLE IF NOT EXISTS TERRITORY_temp (
Territory_Id int(11) NOT NULL,
Territory_Name varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL,
PRIMARY KEY (Territory_Id)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
copy over with
INSERT INTO TERRITORY_temp (Territory_Id, Territory_Name) VALUES
(1, 'SouthEast'),
(2, 'SouthWest'),
(3, 'NorthEast'),
(4, 'NorthWest'),
(5, 'Central');
or
INSERT INTO TERRITORY_temp
SELECT * from TERRITORY_t
add the extra field(s) to match with the new table
copy from the temp table to the destination table
INSERT INTO TERRITORY_new
SELECT * from TERRITORY_temp
Please provide feedback.
Step 1. Create stored procedure
CREATE PROCEDURE CopyDataTable
#SourceTable varchar(255),
#TargetTable varchar(255),
#SourceFilter nvarchar(max) = ''
AS
BEGIN
SET NOCOUNT ON;
DECLARE #SourceColumns VARCHAR(MAX)=''
DECLARE #TargetColumns VARCHAR(MAX)=''
DECLARE #Query VARCHAR(MAX)=''
SELECT
#SourceColumns = ISNULL(#SourceColumns +',', '') + T.COLUMN_NAME
FROM
(
select name as COLUMN_NAME from sys.all_columns
where object_id = (select object_id from sys.tables where name = #SourceTable)
and is_identity = 0
)T
SELECT
#TargetColumns = ISNULL(#TargetColumns +',', '') + T.COLUMN_NAME
FROM
(
select name as COLUMN_NAME from sys.all_columns
where object_id = (select object_id from sys.tables where name = #TargetTable)
and is_identity = 0
)T
set #Query = 'INSERT INTO ' + #TargetTable + ' (' + SUBSTRING(#TargetColumns,2 , 9999) + ') SELECT ' + SUBSTRING(#SourceColumns,2 , 9999) + ' FROM ' + #SourceTable + ' ' + #SourceFilter;
PRINT #Query
--EXEC(#Query)
END
GO
Step 2. Run stored procedure
use YourDatabaseName
exec dbo.CopyDataTable 'SourceTable','TargetTable'
Explanations
a) dbo.CopyDataTable will transfer all data from SourceTable to TargetTable, except field with Identity
b) You can apply filter when call stored procedure, in order to transfer only row based on criteria
exec dbo.CopyDataTable 'SourceTable','TargetTable', 'WHERE FieldName=3'
exec dbo.CopyDataTable 'SourceTable','TargetTable', 'WHERE FieldName=''TextValue'''
c) Remove -- from --EXEC(#Query) WHEN finish

How to dynamically write the query in SQL Server 2008?

How to write the dynamically the below query?
Table
empid designation interestes
1 developer,tester cricket,chess
1 developer chess
1 techlead cricket
Condition:
IF empid = 1
AND (designation LIKE '%developer%' OR designationLIKE '%techlead%')
OR (interests LIKE '%cricket%').
How to write the above query dynamically if designations need to send more than 2,and also same on interstes .
please tell me ...
EDIT stored procedure code:
ALTER PROCEDURE [dbo].[usp_GetDevices]
#id INT,
#designation NVARCHAR (MAX)
AS
BEGIN
declare #idsplat varchar(MAX)
set #idsplat = #UserIds
create table #u1 (id1 varchar(MAX))
set #idsplat = 'insert #u1 select ' + replace(#idsplat, ',', ' union select ')
exec(#idsplat)
Select
id FROM dbo.DevicesList WHERE id=#id AND designation IN (select id1 from #u1)
END
Then when your form is submitted, create a string of designations (should really be a list of foreign keys if you have a 1 to many relationship) and pass that to the SQL. Then parse it into a table using one of many open-source SQL user functions:
-- #designations = 'developer,tester,techlead'
select text_val
from dbo.fn_ParseText2Table(#designations,',')
/* results:
text_val
--------
developer
tester
techlead
*/
Once you have the values in a table you can do any standard join or query operations.