This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 10 years ago.
ID ---- ACCOUNT ---- SECTOR ---- AMOUNT_CURRENCY1 ------ AMOUNT_CURRENCY2
1 --- account1 ---- sector1 --- 100 ------ 200
2 --- account1 ---- sector2 --- 150 ------ 250
3 --- account2 ---- sector1 --- 250 ------ 300
4 --- account2 ---- sector2 --- 90 ------ 180
I need data to be like this
sector1 ------------ sector2
| amount1 | amount2 | amount1 | amount2
Account1 | 100 | 200
Account2 | 250 | 250
I need to put the result in asp.net gridview to edit
I'm using the following script:
1- to get columns:
DECLARE #ColumnHeaders VARCHAR(MAX)
SELECT #ColumnHeaders =
COALESCE(
#ColumnHeaders + ',[!sector:' + cast(sector_ID as nvarchar)+ ':' + sector_name + ']',
'[!sector:' + cast(sector_ID as nvarchar)+ ':' + sector_name+ ']'
)
FROM vw_Transaction
group by sector_ID, sector_name
2- pivot:
DECLARE #TableSQL NVARCHAR(MAX)
SET #TableSQL = N'
SELECT *
FROM (SELECT trans_id, account_name, sector_id, sector_name,
amount_currency1, amount_currency2, ''!sector:'' + cast(sector_ID as nvarchar)+ '':'' + sector_name as col
FROM dbo.vw_Transaction
WHERE
trans_id=' + CAST(#trans_id as varchar) +'
) AS PivotData
PIVOT (
MAX(amount_currency1)
FOR col IN (
' + #ColumnHeaders + '
)
) AS PivotTable'
EXECUTE(#TableSQL)
the problem, i have 2 fields that need to pivot, amount_currency1 and amount_currency2
Since you are trying to PIVOT on two values, you must first UNPIVOT the AMOUNT_CURRENCY1 and AMOUNT_CURRENCY2 columns:
You will want to add something like this to your query:
select account, value, sector+ '_'+col as col
from
(
select sector,
account,
AMOUNT_CURRENCY1,
AMOUNT_CURRENCY2
from vw_Transaction
) src
unpivot
(
value
for col in (AMOUNT_CURRENCY1, AMOUNT_CURRENCY2)
) unpiv
Then your final query will be similar to this:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT DISTINCT ','
+ quotename(t.sec+'_'+c.name)
from
(
select sector sec
from vw_Transaction
) t
cross apply sys.columns as C
where C.object_id = object_id('vw_Transaction') and
C.name not in ('id', 'account', 'sector')
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT account,' + #cols + ' from
(
select account, value,
sector+ ''_''+col as col
from
(
select sector,
account,
AMOUNT_CURRENCY1,
AMOUNT_CURRENCY2
from vw_Transaction
) src
unpivot
(
value
for col in (AMOUNT_CURRENCY1, AMOUNT_CURRENCY2)
) unpiv
) x
pivot
(
max(value)
for col in (' + #cols + ')
) p '
execute(#query)
See SQL Fiddle with Demo
Result:
| ACCOUNT | SECTOR1_AMOUNT_CURRENCY1 | SECTOR1_AMOUNT_CURRENCY2 | SECTOR2_AMOUNT_CURRENCY1 | SECTOR2_AMOUNT_CURRENCY2 |
------------------------------------------------------------------------------------------------------------------------
| account1 | 100 | 200 | 150 | 250 |
| account2 | 250 | 300 | 90 | 180 |
Related
Consider a database with three tables:
goods (Id is the primary key)
+----+-------+-----+
| Id | Name | SKU |
+----+-------+-----+
| 1 | Nails | 123 |
| 2 | Nuts | 456 |
| 3 | Bolts | 789 |
+----+-------+-----+
invoiceheader (Id is the primary key)
+----+--------------+-----------+---------+
| Id | Date | Warehouse | BuyerId |
+----+--------------+-----------+---------+
| 1 | '2021-10-15' | 1 | 223 |
| 2 | '2021-09-18' | 1 | 356 |
| 3 | '2021-07-13' | 2 | 1 |
+----+--------------+-----------+---------+
invoiceitems (Id is the primary key)
+----+----------+--------+-----+-------+
| Id | HeaderId | GoodId | Qty | Price |
+----+----------+--------+-----+-------+
| 1 | 1 | 1 | 15 | 1.1 |
| 2 | 1 | 3 | 7 | 1.5 |
| 3 | 2 | 1 | 12 | 1.5 |
| 4 | 3 | 3 | 3 | 1.3 |
+----+----------+--------+-----+-------+
What I'm trying to do is to get the MAX(invoiceheader.Date) for every invoiceitems.GoodId. Or, in everyday terms, to find out, preferably in a single query, when was the last time any of the goods were sold, from a specific warehouse.
To do that, I'm using a derived query, and the solution proposed here . In order to be able to do that, I think that I need to have a way of giving multiple (well, two) aliases for a derived table.
My query looks like this at the moment:
SELECT tmp.* /* placing the second alias here, before or after tmp.* doesn't work */
FROM ( /* placing the second alias, tmpClone, here also doesn't work */
SELECT
invoiceheader.Id,
invoiceheader.Date,
invoiceitems.HeaderId,
invoiceitems.Id,
invoiceitems.GoodId
FROM invoiceheader
LEFT JOIN invoiceitems
ON invoiceheader.Id = invoiceitems.HeaderId
WHERE invoiceheader.Warehouse = 3
AND invoiceheader.Date > '0000-00-00 00:00:00'
AND invoiceheader.Date IS NOT NULL
AND invoiceheader.Date > ''
AND invoiceitems.GoodId > 0
ORDER BY
invoiceitems.GoodId ASC,
invoiceheader.Date DESC
) tmp, tmpClone /* this doesn't work with or without a comma */
INNER JOIN (
SELECT
invoiceheader.Id,
MAX(invoiceheader.Date) AS maxDate
FROM tmpClone
WHERE invoiceheader.Warehouse = 3
GROUP BY invoiceitems.GoodId
) headerGroup
ON tmp.Id = headerGroup.Id
AND tmp.Date = headerGroup.maxDate
AND tmp.HeaderId = headerGroup.Id
Is it possible to set multiple aliases for a single derived table? If it is, how should I do it?
I'm using 5.5.52-MariaDB.
you can use both (inner select) and left join to achieve this for example:
select t1.b,(select t2.b from table2 as t2 where t1.x=t2.x) as 'Y' from table as t1 Where t1.y=(select t3.y from table3 as t3 where t2.a=t3.a)
While this doesn't answer my original question, it does solve the problem from which the question arose, and I'll leave it here in case anyone ever comes across a similar issue.
The following query does what I'd intended to do - find the newest sale date for the goods from the specific warehouse.
SELECT
invoiceheader.Id,
invoiceheader.Date,
invoiceitems.HeaderId,
invoiceitems.Id,
invoiceitems.GoodId
FROM invoiceheader
INNER JOIN invoiceitems
ON invoiceheader.Id = invoiceitems.HeaderId
INNER JOIN (
SELECT
MAX(invoiceheader.Date) AS maxDate,
invoiceitems.GoodId
FROM invoiceheader
INNER JOIN invoiceitems
ON invoiceheader.Id = invoiceitems.HeaderId
WHERE invoiceheader.Warehouse = 3
AND invoiceheader.Date > '0000-00-00 00:00:00'
AND invoiceheader.Date IS NOT NULL
AND invoiceheader.Date > ''
GROUP BY invoiceitems.GoodId
) tmpDate
ON invoiceheader.Date = tmpDate.maxDate
AND invoiceitems.GoodId = tmpDate.GoodId
WHERE invoiceheader.Warehouse = 3
AND invoiceitems.GoodId > 0
ORDER BY
invoiceitems.GoodId ASC,
invoiceheader.Date DESC
The trick was to join by taking into consideration two things - MAX(invoiceheader.Date) and invoiceitems.GoodId - since one GoodId can only appear once inside a specific invoiceheader / invoiceitems JOINing (strict limit imposed on the part of the code which inserts into invoiceitems).
Whether this is the most optimal solution (ignoring the redundant conditions in the query), and whether it would scale well, remains to be seen - it has been tested on tables with ~5000 entries for invoiceheader, ~60000 entries for invoiceitems, and ~4000 entries for goods. Execution time was < 1 sec.
This may be a kind of the Knapsack problem.
I need to traverse a data table, group it by a column, choosing ones with better time.
Then repeat the previous step until a limit given by column CAPACITY is not reached.
This is the demo scenario:
create table if not exists data( vid num, size num, epid num, sid num, capacity num, dt );
delete from data;
insert into data(vid,size,epid,sid,capacity,dt)
values
(0,20,1,1,50,1100), -- 2nd choice
(0,20,1,1,50,1000), -- 1st choice
(0,20,1,1,50,1200), -- last choice excluded because out of capacity
(1,20,2,2,50,1100), -- 2nd choice
(1,20,2,2,50,1000), -- 1st choice
(1,20,2,2,50,1200); -- last choice excluded because out of capacity
This is the non recursive solution:
with best0 as (
select a.rowid as tid,a.vid,a.sid,a.size,a.dt,a.capacity-a.size as remains,0 as level
from data a
group by a.sid
having min(a.dt)
),
best1 as (
select a.tid,a.vid,a.sid,a.size,a.dt,a.remains, a.level
from (
select
a.rowid as tid,a.sid,a.vid,a.size,a.capacity,a.dt,b.remains-a.size as remains,
b.level+1 as level
from data a
join best0 b on b.sid=a.sid -- and b.level=a.level-1
where not a.rowid in (select tid from best0)
and b.remains-a.size>0
) a group by a.sid having min(a.dt)
),
best2 as (
select a.tid,a.vid,a.sid,a.size,a.dt,a.remains, a.level
from (
select
a.rowid as tid,a.sid,a.vid,a.size,a.capacity,a.dt,b.remains-a.size as remains,
b.level+1 as level
from data a
join best1 b on b.sid=a.sid -- and b.level=a.level-1
where not a.rowid in (select tid from best0 union all select tid from best1)
and b.remains-a.size>0
) a group by a.sid having min(a.dt)
)
select * from best0
union all
select * from best1
union all
select * from best2
And this the result:
tid | vid | sid | size | Dtime | capacity | group_level
--- | --- | --- | ---- | ----- | -------- | -----------
2 | 0 | 1 | 20 | 1000 | 30 | 0
5 | 1 | 2 | 20 | 1000 | 30 | 0
1 | 0 | 1 | 20 | 1100 | 10 | 1
4 | 1 | 2 | 20 | 1100 | 10 | 1
This is the recursive version that give error: "recursive reference in a subquery: best"
with recursive best(tid,vid,sid,size,dt,remains,level)
as (
select a.rowid as tid,a.vid,a.sid,a.size,a.dt,a.capacity-a.size as remains,0 as level
from data a
group by a.sid
having min(a.dt)
union all
select a.tid,a.vid,a.sid,a.size,a.dt,a.remains, a.level
from (
select
a.rowid as tid,a.sid,a.vid,a.size,a.dt,b.remains-a.size as remains,
b.level+1 as level
from data a
join best b on b.sid=a.sid -- and b.level=a.level-1
where not a.rowid in (select tid from best) and b.remains-a.size>0
) a group by a.sid having min(a.dt)
)
select * from best
I tried differents solutions even using a loop counter but everyone give the same error.
i have the following table friend
id | first_name | last_name | gender | age | mobile
1 | bobby | roe | male | 21 | 541-5780
how to concatenate multiple column (first_name & last_name) values into a single column to get the following result?
full_name
bobby roe
i have writen the following query but it does not work
declare #full_name varchar(max)
select #full_name = COALESCE(#full_name + ', ', '') + first_name, last_name
from friend
select #full_name
More than one way to achieve this:
SELECT CONCAT(first_name, ' ' ,last_name) AS full_name;
For earlier versions (Where CONCAT is not a built in function):
SELECT first_name + ISNULL(' ' + last_name, '') as Full_Name from [YourTable]
This as well should give you the same result
SELECT COALESCE(first_name, '') + COALESCE(last_name, '') as FullName FROM [YourTable]
Is there any nice trick to change values in string using dictionary mapping? For example I have table1 FIDDLE
+---------------------------+
| ROW1 |
+---------------------------+
| This is an example string |
| This String has typ0s |
+---------------------------+
And some mapping table dict1 FIDDLE:
+-------------------------+
| OLD | NEW |
+-------------------------+
| THIS | THAT |
| IS | ARE |
| EXAMPLE | SOURCE |
| STRING | NUMBER |
+------------+------------+
I need some SELECT statement that will split values in table1.row1 and change words using mapping dictionary dict1 so received values will be ( changing no existing dictionary values to upper is optional):
+---------------------------+
| TRANS_ROW1 |
+---------------------------+
| THAT ARE AN SOURCE NUMBER |
| THAT NUMBER HAS TYP0S |
+---------------------------+
PS. Spliting using REGEXP expression will be so nice..
WITH dict1 AS
(SELECT 'THIS' fr,
'THAT' t
FROM dual
UNION ALL
SELECT 'IS' fr,
'ARE' t
FROM dual
UNION ALL
SELECT 'EXAMPLE' fr,
'SOURCE' t
FROM dual
UNION ALL
SELECT 'STRING' fr,
'NUMBER' t
FROM dual),
table1 AS
(SELECT 'This is an example string' AS str,
1 AS sn
FROM dual
UNION ALL
SELECT 'This String has typ0s' AS str,
2 sn
FROM dual),
src AS
(SELECT regexp_substr(upper(s.str), '[^ ]+', 1, LEVEL) str2,
s.*,
rownum nn
FROM table1 s
CONNECT BY instr(TRIM(' ' FROM str), ' ', 1, LEVEL - 1) > 0
AND PRIOR sn = sn
AND PRIOR dbms_random.value IS NOT NULL),
repl AS
(SELECT nvl2(dict1.t, dict1.t, src.str2) lex,
sn,
nn
FROM src
LEFT JOIN dict1
ON dict1.fr = src.str2)
SELECT listagg(lex, ' ') within GROUP(ORDER BY nn),
sn
FROM repl
GROUP BY sn
It works now as you ask. Enjoy.
EDIT: FIDDLE with solution
I have 4 columns in a table called musics - 'artist','genre', 'writer' , 'producer'.
I need to write a query such that, it returns a value 0 , if there are no repetition of values corresponding to the column name; if there is a repetition of values, it should return a value 1, corresponding to that column name.
Any help is much appreciated
SELECT (COUNT(artist) <> COUNT(DISTINCT artist)) artist,
(COUNT(genre) <> COUNT(DISTINCT genre)) genre,
(COUNT(writer) <> COUNT(DISTINCT writer)) writer,
(COUNT(producer) <> COUNT(DISTINCT producer)) producer
FROM musics
Another version
SELECT
( SELECT (COUNT(*) > 0)
FROM (SELECT 1 FROM musics GROUP BY artist HAVING COUNT(*) > 1) a
) artist,
( SELECT (COUNT(*) > 0)
FROM (SELECT 1 FROM musics GROUP BY genre HAVING COUNT(*) > 1) g
) genre,
( SELECT (COUNT(*) > 0)
FROM (SELECT 1 FROM musics GROUP BY writer HAVING COUNT(*) > 1) w
) writer,
( SELECT (COUNT(*) > 0)
FROM (SELECT 1 FROM musics GROUP BY producer HAVING COUNT(*) > 1) p
) producer
Sample data
| artist | genre | writer | producer |
------------------------------------------
| artist1 | genre1 | writer1 | producer1 |
| artist2 | genre2 | writer1 | producer2 |
| artist1 | genre3 | writer3 | producer3 |
Sample output:
| artist | genre | writer | producer |
--------------------------------------
| 1 | 0 | 1 | 0 |
SQLFiddle
For Artist
select convert(bit,(count(1)-1))
from table_name
group by artist -- <-- Replace artist with column name for which duplicate
write a select count statement using distinct with specified column and another select count without distinct and compare both of them based on your requirement
you can use 4 different query with union & each query must contain count(column name) + group by clause