I am using Python 3 and PyQt 5 to execute a query that includes a table variable. My other queries work, but this specific one is causing me issues. The query is:
declare #Temp table
(
SKU varchar(25),
Lines int
)
insert into #Temp
SELECT convert(varchar, [Sku]) as [SKU]
,count([Order Number]) as [Lines]
from [Database].dbo.[Table Name]
GROUP BY [Sku]
ORDER BY [LINES]
Select RANK() OVER(Order by Lines Desc) / (Select Convert(float,Count(*)) from #Temp) as [CUM PERCENT SKU]
,sum(Lines) Over(Order by Lines Desc) / convert(float,sum(Lines) Over()) as [CUM PERCENT LINES]
from #Temp
Order By [Lines] DESC
I replaced the Database name and the Table name, but the rest of the text is the exact query.
Executing the above in SQL Server 2012 works perfectly. Is there a limitation to QSql that I don't know about?
Thank you in advance!
After a bit more research, it turns out that you need to use prepare() when you are trying to create a table (apparently even a temporary one).
I'm posting the answer in case anyone else has the same issue and doesn't feel like going through the C++ StackOverflow answers.
query = QSqlQuery()
query.prepare(*query text here*)
query.exec_()
Related
I am very new to SQL Server and it stucks in one task related to the aggregation of data. I tried to put all information on the screen attached. Is there anyone that could support it? My current code is below, but it does not work (duplicates...). Many thanks in advance!
select distinct
material,
plant,
STRING_AGG (exceptions, ';' ),
STRING_AGG (preferential_country, ';' )
from grs
GROUP BY material, plant
Sorry, I misunderstood part of your question. This is a bit messy, but I created a function to remove the duplicates.
CREATE FUNCTION remove_duplicate (#str varchar(100))
RETURNS varchar(100) AS
BEGIN
DECLARE #result varchar(100)
SET #result = (SELECT STRING_AGG(ta.value,';') from(
SELECT DISTINCT value FROM STRING_SPLIT(#str ,';')
)as ta)
RETURN #result
END;
This is the query to create the output table where the exceptions are group together but there will be duplicates
SELECT distinct plant,
material,
CASE WHEN count( distinct(preferential_country)) > 1 THEN 'QU'
ELSE MAX(preferential_country)
END AS preferential_country,
STRING_AGG(exceptions,';') as exceptions
into new_table
FROM tableA
group by plant, material
order by plant
Lastly, you just need to use the function to remove the duplicates.
SELECT plant, material, dbo.remove_duplicate(exceptions) as exceptions from new_table
output:
plant material exceptions
plant_1 Material_1 EU01;EU02;EU03;EU05
plant_1 Material_2 EU01
plant_2 Material_1 NULL
I couldn't get it to work in db fiddle but it's working on SSMS
db fiddle
I am trying to run this SQL statement:
select *
from table
where 1 = 1
and date >= '1/1/2020'
and id = any (**'list of 1300 items'**)
order by date asc;
The issue is that my list in the second and statement is actually over 1000 expressions, so it does not let me run this statement. Does anyone know a better way to do this? Basically, I have and ID that I want to locate in our database and the easiest way I know to do this is with a list but clearly mine is too long. I'm new to this so any insight would be greatly appreciated!
There are multiple ways to do that. For example, if your SQL server version is not old, you could use openJSON(). ie:
DECLARE #ids VARCHAR(MAX) = '[1,3,34,434,43]' -- your 1300+ ids
select *
from yourtable
where [date] >= '20200101'
and id IN (SELECT [value] FROM OPENJSON(#ids))
order by [date] asc;
You can think of string_split, if your SQL Server version is 2016 or later. Thanks to #Cetin Bazos for the basic script.
DECLARE #ids NVARCHAR(MAX) = '1,3,34,434,43' -- your 1300+ ids
select *
from yourtable
where [date] >= '20200101'
and id IN (SELECT [value] FROM STRING_SPLIT(#ids,','))
order by [date] asc;
Consider the following query...
SELECT
*
,CAST(
(CurrentSampleDateTime - PreviousSampleDateTime) AS FLOAT
) * 24.0 * 60.0 AS DeltaMinutes
FROM
(
SELECT
C.SampleDateTime AS CurrentSampleDateTime
,C.Location
,C.CurrentValue
,(
SELECT TOP 1
Previous.SampleDateTime
FROM Samples AS Previous
WHERE
Previous.Location = C.Location
AND Previous.SampleDateTime < C.SampleDateTime
ORDER BY Previous.SampleDateTime DESC
) AS PreviousSampleDateTime
FROM Samples AS C
) AS TempResults
Assuming all things being equal such as indexing, etc is this the most efficient way of achieving the above results? That is using a SubQuery to retrieve the last record?
Would I be better off creating a cursor that orders by Location, SampleDateTime and setting up variables for CurrentSampleDateTime and PreviousSampleDateTime...setting the Previous to the Current at the bottom of the while loop?
I'm not very good with CTE's is this something that could be accomplished more efficiently with a CTE? If so what would that look like?
I'm likely going to have to retrieve PreviousValue along with Previous SampleDateTime in order to get an average of the two. Does that change the results any.
Long story short what is the best/most efficient way of holding onto the values of a previous record if you need to use those values in calculations on the current record?
----UPDATE
I should note that I have a clustered index on Location, SampleDateTime, CurrentValue so maybe that is what is affecting the results more than anything.
with 5,591,571 records my query (the one above) on average takes 3 mins and 20 seconds
The CTE that Joachim Isaksson below on average is taking 5 mins and 15 secs.
Maybe it's taking longer because it's not using the clustered index but is using the rownumber for the joins?
I started testing the cursor method but it's already at 10 minutes...so no go on that one.
I'll give it a day or so but think I will accept the CTE answer provided by Joachim Isaksson just because I found a new method of getting the last row.
Can anyone concur that it's the index on Location, SampleDateTime, CurrentValue that is making the subquery method faster?
I don't have SQL Server 2012 so can't test the LEAD/LAG method. I'd bet that would be quicker than anything I've tried assuming Microsoft implemented that efficiently. Probably just have to swap a pointer to a memory reference at the end of each row.
If you are using SQL Server 2012, you can use the LAG window function that retrieves the value of the specified column from the previous row. It returns null if there is no previous row.
SELECT
a.*,
CAST((a.SampleDateTime - LAG(a.SampleDateTime) OVER(PARTITION BY a.location ORDER BY a.SampleDateTime ASC)) AS FLOAT)
* 24.0 * 60.0 AS DeltaMinutes
FROM samples a
ORDER BY
a.location,
a.SampleDateTime
You'd have to run some tests to see if it's faster. If you're not using SQL Server 2012 then at least this may give others an idea of how it can be done with 2012. I like #Joachim Isaksson 's answer using a CTE with a Row_Number()/Partition By for 2008 and 2005.
SQL Fiddle
Have you considered creating a temp table to use instead of a CTE or subquery? You can create indexes on the temp table that are more suited for the join on RowNumber.
CREATE TABLE #tmp (
RowNumber INT,
Location INT,
SampleDateTime DATETIME,
CurrentValue INT)
;
INSERT INTO #tmp
SELECT
ROW_NUMBER() OVER (PARTITION BY Location
ORDER BY SampleDateTime DESC) rn,
Location,
SampleDateTime,
CurrentValue
FROM Samples
;
CREATE INDEX idx_location_row ON #tmp(Location,RowNumber) INCLUDE (SampleDateTime,CurrentValue);
SELECT
a.Location,
a.SampleDateTime,
a.CurrentValue,
CAST((a.SampleDateTime - b.SampleDateTime) AS FLOAT) * 24.0 * 60.0 AS DeltaMinutes
FROM #tmp a
LEFT JOIN #tmp b ON
a.Location = b.Location
AND b.RowNumber = a.RowNumber +1
ORDER BY
a.Location,
a.SampleDateTime
SQL Fiddle #2
As always, testing with your real data is king.
Here's a CTE version that shows the samples for each location with time deltas from the previous sample. It uses OVER ranking, which usually does well in comparison to subqueries for solving the same problem.
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY Location
ORDER BY SampleDateTime DESC) rn
FROM Samples
)
SELECT a.*,CAST((a.SampleDateTime - b.SampleDateTime) AS FLOAT)
* 24.0 * 60.0 AS DeltaMinutes
FROM cte a
LEFT JOIN cte b ON a.Location = b.Location AND b.rn = a.rn +1
An SQLfiddle to test with.
I have a SQL Server table with the following structure
cod_turn (PrimaryKey)
taken (bit)
time (datetime)
and several other fields which are irrelevant to the problem. I cant alter the table structure because the app was made by someone else.
given a numeric variable parameter, which we will assume to be "3" for this example, and a time, I need to create a query which looking from that time on, it looks the first 3 consecutive records which are not marked as "taken". I cant figure out how to make the query in pure sql, if possible.
PS: I accepted the answer because it was correct, but I made a bad description of the problem. I will open another question later. Feeling stupid after seeing the size of the answers =)
SELECT TOP 3 * FROM table WHERE taken = 0 AND time>=#Time ORDER BY time
Where #Time is whatever time you pass in.
Assuming current versions of SQL Server and assuming you've named you "numeric variable parameter" as #top int. Note:the parenthesis around #top are required when using a parameter-ized TOP
SELECT TOP (#top)
cod_turn,
taken ,
time
FROM yourtable
WHERE Taken = 0 AND time>=#Time
ORDER BY time DESC
You can also do
with cte as
(
SELECT
ROW_NUMBER() over (order by time desc) rn
cod_turn,
taken ,
time
FROM yourtable
WHERE Taken = 0 AND time>=#Time
)
SELECT
cod_turn,
taken ,
time
FROM CTE
WHERE rn <= #top
ORDER BY time DESC
SELECT TOP 3
*
FROM
table
WHERE
time >= #inserted_time
AND taken = 0
ORDER BY
cod_turn ASC
select MT.*
from
(
select cod_turn, ROW_NUMBER() OVER (ORDER BY cod_turn) [RowNumber] -- or by time
from myTable
where taken = 0
and time >= #myTime
) T
inner join myTable MT on MT.cod_turn = T.cod_turn
where T.RowNumber < #myNumber
select top 3 * from theTable where taken = 0 and time > theTime orderby time
How do I page results in SQL Server 2005?
I tried it in SQL Server 2000, but there was no reliable way to do this. I'm now wondering if SQL Server 2005 has any built in method?
What I mean by paging is, for example, if I list users by their username, I want to be able to only return the first 10 records, then the next 10 records and so on.
Any help would be much appreciated.
You can use the Row_Number() function.
Its used as follows:
SELECT Row_Number() OVER(ORDER BY UserName) As RowID, UserFirstName, UserLastName
FROM Users
From which it will yield a result set with a RowID field which you can use to page between.
SELECT *
FROM
( SELECT Row_Number() OVER(ORDER BY UserName) As RowID, UserFirstName, UserLastName
FROM Users
) As RowResults
WHERE RowID Between 5 AND 10
etc
If you're trying to get it in one statement (the total plus the paging). You might need to explore SQL Server support for the partition by clause (windowing functions in ANSI SQL terms). In Oracle the syntax is just like the example above using row_number(), but I have also added a partition by clause to get the total number of rows included with each row returned in the paging (total rows is 1,262):
SELECT rn, total_rows, x.OWNER, x.object_name, x.object_type
FROM (SELECT COUNT (*) OVER (PARTITION BY owner) AS TOTAL_ROWS,
ROW_NUMBER () OVER (ORDER BY 1) AS rn, uo.*
FROM all_objects uo
WHERE owner = 'CSEIS') x
WHERE rn BETWEEN 6 AND 10
Note that I have where owner = 'CSEIS' and my partition by is on owner. So the results are:
RN TOTAL_ROWS OWNER OBJECT_NAME OBJECT_TYPE
6 1262 CSEIS CG$BDS_MODIFICATION_TYPES TRIGGER
7 1262 CSEIS CG$AUS_MODIFICATION_TYPES TRIGGER
8 1262 CSEIS CG$BDR_MODIFICATION_TYPES TRIGGER
9 1262 CSEIS CG$ADS_MODIFICATION_TYPES TRIGGER
10 1262 CSEIS CG$BIS_LANGUAGES TRIGGER
The accepted answer for this doesn't actually work for me...I had to jump through one more hoop to get it to work.
When I tried the answer
SELECT Row_Number() OVER(ORDER BY UserName) As RowID, UserFirstName, UserLastName
FROM Users
WHERE RowID Between 0 AND 9
it failed, complaining that it didn't know what RowID was.
I had to wrap it in an inner select like this:
SELECT *
FROM
(SELECT
Row_Number() OVER(ORDER BY UserName) As RowID, UserFirstName, UserLastName
FROM Users
) innerSelect
WHERE RowID Between 0 AND 9
and then it worked.
When I need to do paging, I typically use a temporary table as well. You can use an output parameter to return the total number of records. The case statements in the select allow you to sort the data on specific columns without needing to resort to dynamic SQL.
--Declaration--
--Variables
#StartIndex INT,
#PageSize INT,
#SortColumn VARCHAR(50),
#SortDirection CHAR(3),
#Results INT OUTPUT
--Statements--
SELECT #Results = COUNT(ID) FROM Customers
WHERE FirstName LIKE '%a%'
SET #StartIndex = #StartIndex - 1 --Either do this here or in code, but be consistent
CREATE TABLE #Page(ROW INT IDENTITY(1,1) NOT NULL, id INT, sorting_1 SQL_VARIANT, sorting_2 SQL_VARIANT)
INSERT INTO #Page(ID, sorting_1, sorting_2)
SELECT TOP (#StartIndex + #PageSize)
ID,
CASE
WHEN #SortColumn='FirstName' AND #SortDirection='ASC' THEN CAST(FirstName AS SQL_VARIANT)
WHEN #SortColumn='LastName' AND #SortDirection='ASC' THEN CAST(LastName AS SQL_VARIANT)
ELSE NULL
END AS sort_1,
CASE
WHEN #SortColumn='FirstName' AND #SortDirection='DES' THEN CAST(FirstName AS SQL_VARIANT)
WHEN #SortColumn='LastName' AND #SortDirection='DES' THEN CAST(LastName AS SQL_VARIANT)
ELSE NULL
END AS sort_2
FROM (
SELECT
CustomerId AS ID,
FirstName,
LastName
FROM Customers
WHERE
FirstName LIKE '%a%'
) C
ORDER BY sort_1 ASC, sort_2 DESC, ID ASC;
SELECT
ID,
Customers.FirstName,
Customers.LastName
FROM #Page
INNER JOIN Customers ON
ID = Customers.CustomerId
WHERE ROW > #StartIndex AND ROW <= (#StartIndex + #PageSize)
ORDER BY ROW ASC
DROP TABLE #Page
I believe you'd need to perform a separate query to accomplish that unfortionately.
I was able to accomplish this at my previous position using some help from this page:
Paging in DotNet 2.0
They also have it pulling a row count seperately.
Here's what I do for paging: All of my big queries that need to be paged are coded as inserts into a temp table. The temp table has an identity field that will act in a similar manner to the row_number() mentioned above. I store the number of rows in the temp table in an output parameter so the calling code knows how many total records there are. The calling code also specifies which page it wants, and how many rows per page, which are selected out from the temp table.
The cool thing about doing it this way is that I also have an "Export" link that allows you to get all rows from the report returned as CSV above every grid in my application. This link uses the same stored procedure: you just return the contents of the temp table instead of doing the paging logic. This placates users who hate paging, and want to see everything, and want to sort it in a million different ways.