First I will show you example data, expected input and output:
VALUE1 | QTY
-------------
111-01 | 5
111-02 | 3
111-03 | 2
112-01 | 4
Expected input from user is VALUE1 or list of VALUE1 ( in SSRS multiple value, variable TEXT).
Expected output is for example SUM of QTY for each VALUE1 selected by user, but with this condition
like SUBSTRING(VALUE1,1,3)+'%'
In this case for user selection 111-01 output is
VALUE1 | QTY
-------------
111 | 10
So far it seems like LIKE operator in IN statement. I have found only solution which is to split the parameter from SSRS and do some loop as (pseudocode)
foreach #parameter in #parameter.Split
where VALUE1 like '#parameter[0]'+'%' or ...
I think there is some more elegant solution. Anyway, this solution is really slow. I am not much experienced with SSRS so maybe some grouping after dataset is created can be solution.
Probably you might want to try:
pseudocode:
WITH condition
AS ( SELECT SUBSTRING(c.SplitValue, 1, 3) Criteria
FROM dbo.fncSplit('111-1,112-2,113-3,114-4,115-1,116-1', ',') c
)
SELECT SUM(t.QTY)
FROM dbo.tblTest t
INNER JOIN condition con ON con.Criteria = SUBSTRING(t.Value1, 1, 3)
try to do a full text index on the table and the performance might be improved
Related
Forgive me, but I can't get this working.
I can find lots of complex pivots using numeric values, but nothing basic based on strings to build upon.
Lets suppose this is my source query from a temp table. I can't change this:
select * from #tmpTable
This provides 12 rows:
Row | Name | Code
---------------------------------
1 | July 2019 | 19/20-01
2 | August 2019 | 19/20-02
3 | September 2019 | 19/20-03
.. .. ..
12 | June 2020 | 19/20-12
I want to pivot this and return the data like this:
Data Type | [0] | [1] | [3] | [12]
---------------------------------------------------------------------------
Name | July 2019 | August 2019 | September 2019 | June 2020
Code | 19/20-01 | 19/20-02 | 19/20-03 | 19/20-12
Thanks in advance..
Strings and numbers aren't much different in pivot terms, it's just that you can't use numeric aggregators like SUM or AVG on them. MAX will be fine and in this case you'll only have one Value so nothing will be lost
You need to pull your data out to a taller key/value representation before pivoting it back to look the other way round as it does now
unpivot the data:
WITH upiv AS(
SELECT 'Name' as t, row as r, name as v FROM #tempTable
UNION ALL
SELECT 'Code' as t, row, code FROM #tempTable
)
Now the data can be re grouped and conditionally aggregated on the r columns:
SELECT
t,
MAX(CASE WHEN r = 1 THEN v END) as r1,
MAX(CASE WHEN r = 2 THEN v END) as r2,
...
MAX(CASE WHEN r = 12 THEN v END) as r12
FROM
upiv
GROUP BY
t
You'll need to put the two sql blocks I present here together so they form a single sql statement. If you want to know more about how this works, I suggest you run the sql statement inside the with block, take a look at it, and also remove the group by/max words from the full statement and look at the result. You'll see the WITH block query makes the data taller, essentially a key/value pair that is tracking what type the data is (name or code). When you run the full sql without the group by/max you'll see the tall data spreads out sideways to give a lot of nulls and a diagonal set of cell data (if ordered by r). The group by collapses all these nulls because a MAX will pick any value over null (of which there is only one)
You could also do this as an UNPIVOT followed by a PIVOT. I've always preferred to use this form because not every database supports the UN/PIVOT keywords. Arguably, UNPIVOT/PIVOT could perform better because there may be specific optimizations the developers can make (eg UNPIVOT could single scan a table; this multiple Union approach may require multiple scans and ways round it could be more memory intensive) but in this case it's only 12 rows. I suspect you're using SQLServer but if you're using a database that doesn't understand WITH you can place the bracketed statement of the WITH (including the brackets) between the FROM and the upiv to make it a subquery if the pattern SELECT ... FROM (SELECT ... UNION ALL SELECT ...) upiv GROUP BY ...; there is no difference
I'll leave renaming the output columns as an exercise for you but I would urge you to consider not putting spaces or square brackets in the column names as you show in your question
I'm looking for a way to perform a SQL type command in Excel. I need to get a count of each string in a column without knowing the string's text before hand.
Here's some sample data, I want to get a count of each Name.
Name
----
A
B
C
A
D
B
IN SQL I'd
SELECT Name, count(*)
FROM #table
group by Name
And I'd expect to get
Name | Count
-----|------
A | 2
B | 2
C | 1
D | 1
How can I perform this operation in Excel?
You could go with the pivot tables that give you some options to analyze your data. There is a good example with explanation on this website: http://www.contextures.com/pivottablecountunique.html
Another SQL question. I have the following query:
SELECT EXTRACT(epoch from dt) as diff
from (
SELECT time_col - lag(time_col) OVER dt
FROM myTable where elementID=1234
) as dt
This calculates the time difference and I get e.g. the following result table for ElementID 1234:
34
345
6
2
...
However I want to do this for every element ID that is stored in the table elementTable. Here is my approach:
SELECT EXTRACT(epoch from dt) as diff
from (
SELECT time_col - lag(time_col) OVER dt
FROM myTable where elementID=any(select elementID from elementTable)
) as dt
This is very close to the wanted result, however I get everything in a single column. E.g.
34 <- For element id = 1234
345 <- For element id = 1234
6 <- For element id = 1234
2 <- For element id = 1234
83 <- For element id = x
4 <- For element id = x
6 <- For element id = x
...
What I want to have is this (ordered in columns by element id):
1234 | x | ...
------------------
34 83 ...
345 4 ...
6 6 ...
2
Sorry for bothering you with my SQL questions, I'm trying to learn...
Use a simple JOIN instead of the convoluted ANY construct:
SELECT element_id
,EXTRACT(epoch from (time_col - lag(time_col) OVER (ORDER BY something)))
FROM tbl
JOIN elementTable USING (element_id);
Also, you broke the valid solution #Clodoaldo provided for your previous question: the OVER clause dt for the window function was undefined in your query.
To get the result you are asking for, look into the crosstab() function of the tablefunc module. More info here:
Sum by month and put months as columns
Dynamic number of columns
"Dynamic" makes this a lot harder, since SQL wants to know the resulting columns beforehand. I covered the topic comprehensively in this related answer:
Dynamic alternative to pivot with CASE and GROUP BY
If an array instead of individual columns is good enough for you, look at the answer to the same question by #Clodoaldo, or, for more explanation, the chapter "Variable number of columns sharing the same type" in this related answer:
Refactor a PL/pgSQL function to return the output of various SELECT queries
I'm quite new into SQL and I'd like to make a SELECT statement to retrieve only the first row of a set base on a column value. I'll try to make it clearer with a table example.
Here is my table data :
chip_id | sample_id
-------------------
1 | 45
1 | 55
1 | 5986
2 | 453
2 | 12
3 | 4567
3 | 9
I'd like to have a SELECT statement that fetch the first line with chip_id=1,2,3
Like this :
chip_id | sample_id
-------------------
1 | 45 or 55 or whatever
2 | 12 or 453 ...
3 | 9 or ...
How can I do this?
Thanks
i'd probably:
set a variable =0
order your table by chip_id
read the table in row by row
if table[row]>variable, store the table[row] in a result array,increment variable
loop till done
return your result array
though depending on your DB,query and versions you'll probably get unpredictable/unreliable returns.
You can get one value using row_number():
select chip_id, sample_id
from (select chip_id, sample_id,
row_number() over (partition by chip_id order by rand()) as seqnum
) t
where seqnum = 1
This returns a random value. In SQL, tables are inherently unordered, so there is no concept of "first". You need an auto incrementing id or creation date or some way of defining "first" to get the "first".
If you have such a column, then replace rand() with the column.
Provided I understood your output, if you are using PostGreSQL 9, you can use this:
SELECT chip_id ,
string_agg(sample_id, ' or ')
FROM your_table
GROUP BY chip_id
You need to group your data with a GROUP BY query.
When you group, generally you want the max, the min, or some other values to represent your group. You can do sums, count, all kind of group operations.
For your example, you don't seem to want a specific group operation, so the query could be as simple as this one :
SELECT chip_id, MAX(sample_id)
FROM table
GROUP BY chip_id
This way you are retrieving the maximum sample_id for each of the chip_id.
I have a table with columns like (in sql server 2000)
MailCode Mode Name Group
-------- ----- --------- -------
1 1 abc 0
1 1 def 0
1 1 qwe 1
2 2 aaw 0
2 2 aad 0
I want to group the Name field based on the rest of the fileds so that the result looks like this (there should be only one unique mailCode, Mode and group combination)
MailCode Mode Names Group
--------- ------ ------------ -------
1 1 abc, def 0
1 1 qwe 1
2 2 aaw, aad 0
How can I create the sql query for this?
I had a similar problem where I had to concatenate a field in the select, my solution at the time was to create a procedure that returned the result and called it like this
select x as field1, y as field2, dbo.procedure as field3
SQL Server 2000 solution
Luckily, COALESCE is supported in 2000, so you can use the COALESCE trick to create a comma delimited list of values, demonstrated in this link. Because of the variable usage, you'll need to create a function/procedure and call it within the main query. Basically, just replace the STUFF() in the query below with the function call.
SQL Server 2005+ solution:
SELECT x.mailcode,
x.mode,
STUFF((SELECT y.name
FROM TABLE y
WHERE y.mailcode = x.mailcode
AND y.mode = x.mode
AND y.gropu = x.group
GROUP BY y.mailcode, y.mode, y.group
FOR XML PATH(', ')), 1, 1, '') AS name,
x.group
FROM TABLE x
GROUP BY x.mailcode, x.mode, x.group
I can't think of a simple query that will get the result you're looking for, but some logic along these lines should get you where you want:
1) Loop through distinct MailCode, Mode, Group Rows
A) select all names in group
A.1) Loop through names
A.2) Concatenate them together into temp variable
B) insert all data (MailCode, Mode, Group, temp variable) into temp table
Fair waring, looping in SQL tends to have a huge performance hit when it comes to large datasets. I unfortunately don't know a better way to do it.