Sql to make generic query in order to get resultant table - sql

I have following table named tblItemLocations.
locationName pickRouteOrder ispickable itemnumber
Loc1 124 1 10-001
Loc2 126 0 10-001
Loc3 128 1 10-002
Loc4 130 0 10-004
Loc44 136 0 10-004
Loc5 131 1 10-007
Loc6 133 1 10-008
Needed result :
Foreach itemnumber , there should be only one record with ispickable=1.
If Ispickable=0, then show No Loc.
Also total number of record is equal to total number of distinct itemnumber.
e.g in above table , there are 5 distinct itemnumber (10-001,10-002,10-004,10-007,10-008).
So there should be 5 records in result table.
Resultant table :
locationName pickRouteOrder itemnumber
Loc1 124 10-001
Loc3 128 10-002
No Loc 130 10-004
Loc5 131 10-007
Loc6 133 10-008
Can you please help to make query in order to get resultant table?

Try this (tested using sql-sever and mysql):
select
coalesce( max(case when ispickable = 1 then locationName
else null end ), 'no loc') as locationName,
min( pickRouteOrder ) pickRouteOrder,
itemnumber
from test
group by itemnumber
The above does not generate the expected output when there are multiple records of an item and the one with the minimum pickRouteOrder is non-pickable (ispickable = 0).
Try below instead (modified from Gordon Linoff answer):
select locationName, pickRouteOrder, itemnumber
from test
where ispickable = 1
union all
select 'no loc', min(pickRouteOrder), itemnumber -- use group by to eliminate multiple non-pickable records
from test t
where ispickable = 0 and
not exists (select 1 from test t2 where t2.itemnumber = t.itemnumber
and t2.ispickable = 1)
group by itemnumber;

If at most one row is pickable and unpickable (as with the sample data), you can also approach this as:
select locationName, pickRouteOrder, itemnumber
from t
where ispickable = 1
union all
select 'no loc', pickRouteOrder, itemnumber
from t
where ispickable = 0 and
not exists (select 1 from t t2 where t2.itemnumber = t.itemnumber and t2.ispackable = 1);
Under many circumstances, this will be faster than an aggregation solution, although I also like that version as well.

Related

Calculate product of column values on the basis of other column in SQL Server

I have a table
Tid Did value
------------------
1 123 100
1 234 200
2 123 323
2 234 233
All tids have dids as 123 and 234. So for every tid having dids 123 and 234 I want the product of corresponding values
The output table will be
Tid Product
------------------
1 20000 (product of 100 and 200)
2 75259 (product of 323 and 233)
Any help?
select tid,
min(case when did = 123 then value end)
* min(case when did = 234 then value end) product
from my_table
group by tid
To get the data for multiple rows combined (based on tid) you use GROUP BY.
Because you're grouping by tid, you have to use an aggregate function to do anything with other values from the individual rows. If implied assumptions hold (exactly 1 row matching each did for each tid) then it doesn't matter much what aggregate function you use; min is as good as anything.
Within the aggregation, you use CASE logic to select value for the required did (and NULL for all other rows in the tid group).
Then just do the math.
You can use some arithmetic to get the product per tid.
select tid,exp(sum(log(value))) as prod
from t
group by tid
To do this only for tid's having did values 123 and 234, use
select tid,exp(sum(log(value))) as prod
from t
group by tid
having count(distinct case when did in (123,234) then did end) = 2
Here's a Rexster solution, based on good work of #gbn here
SELECT
Tid,
CASE
WHEN MinVal = 0 THEN 0
WHEN Neg % 2 = 1 THEN -1 * EXP(ABSMult)
ELSE EXP(ABSMult)
END
FROM
(
SELECT
Tid,
SUM(LOG(ABS(NULLIF(value, 0)))) AS ABSMult,
SUM(SIGN(CASE WHEN value < 0 THEN 1 ELSE 0 END)) AS Neg,
MIN(ABS(value)) AS MinVal
FROM
t
GROUP BY
Tid
) t2

SQL, Check if Rows are in another Table

I have two tables, Stock and Warehouse.
I need to get the Items which are available in all Warehouses.
Here an example:
Stock Table:
ItemID WarehouseID ItemAmount
-------------------------------------------
1043 1 20
1043 2 2
1043 3 16
1043 4 17
1044 1 32
1044 2 12
1044 4 7
1055 2 6
 
Warehouse Table:
WarehouseID WarehouseName
-------------------------------
1 Name1
2 Name2
3 Name3
4 Name4
For the Example the result should be Item 1043 because its available in all Warehouses, unlike the other ones.
I didn't get to a solution, can anyone help me?
You could also use this "double negative" query using NOT EXISTS:
SELECT DISTINCT s.ItemID
FROM StockTable s
WHERE NOT EXISTS
(
SELECT 1 FROM Warehouse w
WHERE NOT EXISTS(SELECT 1 FROM StockTable s2
WHERE w.WarehouseID = s2.WarehouseID
AND s.ItemID = s2.ItemID)
)
Demo fiddle
This approach looks more verbose but it has some benefits:
you can change it easily if the rules are getting more complex
you can remove the DISTINCT to see all rows
you can add all columns since GROUP BY was not used
it has no issues with null values
select itemid
from stock
group by itemid
having count(distinct warehouseid) = (select count(*) from warehouse);
SQLFiddle: http://sqlfiddle.com/#!15/e4273/1
If the stock table may also contain items with an amount = 0 you need to add a where clause:
select itemid
from stock
where itemamount > 0
group by itemid
having count(distinct warehouseid) = (select count(*) from warehouse);
NOT EXISTS combined with EXCEPT:
select distinct ItemID
from stock s1
where not exists (select warehouseid from warehouse
except
select warehouseid from stock s2 where s2.ItemID = s1.ItemID);
You can even replace select distinct ItemID with select * to get all those items.
I use this query:
SELECT
ItemID
FROM
stock
GROUP BY
ItemID
HAVING
SUM(DISTINCT warehouseid) = (SELECT SUM(WarehouseID) from warehouse)
That is more reliable than using COUNT, because in a rare situation of don't making a foreign key it should returns some invalid results.

SQL query to get value of another column corresponding to a max value of a column based on group by

I have the following table:
ID BLOWNUMBER TIME LADLE
--- ---------- ---------- -----
124 1 01/01/2012 2
124 1 02/02/2012 1
124 1 03/02/2012 0
124 2 04/01/2012 1
125 2 04/06/2012 1
125 2 01/03/2012 0
I want to have the TIME for the maximum value of LADLE for a group of ID & BLOWNUMBER.
Output required:
124 1 01/01/2012
124 2 04/01/2012
125 2 04/06/2012
If you're using SQL Server (or another engine which supports CTE's and ROW_NUMBER), you can use this CTE (Common Table Expression) query:
;WITH CTE AS
(
SELECT
ID, BlowNumber, [Time],
RN = ROW_NUMBER() OVER (PARTITION BY ID, BLOWNUMBER ORDER BY [Time] DESC)
FROM Sample
)
SELECT *
FROM CTE
WHERE RN = 1
See this SQL Fiddle here for an online live demo.
This CTE "partitions" your data by (ID, BLOWNUMBER), and the ROW_NUMBER() function hands out numbers, starting at 1, for each of those "partitions", ordered by the [Time] columns (newest time value first).
Then, you just select from that CTE and use RN = 1 to get the most recent of each data partition.
If you are using sqllite (probably compatible with other DBs as well); you could do:
select
ct.id
, ct.blownumber
, time
from
new
, (
select
id
, blownumber
, max(ladle) as ldl
from
new
group by
id
, blownumber
) ct
where
ct.id = new.id
and ct.blownumber = new.blownumber
and ct.ldl = new.ladle;

Update column value of one row from other rows

I have the following table:
sno name pid amount total
1 Arif 0 100 null
2 Raj 1 200 null
3 Ramesh 2 100 null
4 Pooja 2 100 null
5 Swati 3 200 null
6 King 4 100 null
I want total of each person such that it gives total sum of amount of its descendants.
For ex.
for RAJ total will be : total= amount of(raj+ramesh+pooja+swati+king)
for SWATI :Total=amount of swati only.
You could try something like this:
WITH hierarchified AS (
SELECT
sno,
amount,
hierarchyID = CAST(sno AS varchar(500))
FROM yourTable
WHERE pid = 0
UNION ALL
SELECT
t.sno,
t.amount,
hierarchyID = CAST(h.hierarchyID + '/' + RTRIM(t.sno) AS varchar(500))
FROM yourTable t
INNER JOIN hierarchified h ON t.pid = h.sno
)
UPDATE yourTable
SET total = t.amount + ISNULL(
(
SELECT SUM(amount)
FROM hierarchified
WHERE hierarchyID LIKE h.hierarchyID + '/%'
),
0
)
FROM yourTable t
INNER JOIN hierarchified h ON t.sno = h.sno;
Note that this query (which you can try on SQL Fiddle) would probably not be very efficient on a large dataset. It might do as a one-off query, and then it would likely be better to organise updating the totals each time the table is updated, i.e. using triggers.

Order given elements with SQL ORDER BY

Given:
SELECT projectID, urlID, COUNT(1) AS totalClicks, projectPage,
(SELECT COUNT(1)
FROM tblStatSessionRoutes, tblStatSessions
WHERE tblStatSessionRoutes.statSessionID = tblStatSessions.ID
AND tblStatSessions.projectID = tblAdClicks.projectID
AND (tblStatSessionRoutes.leftPageID = tblAdClicks.projectPage OR
tblStatSessionRoutes.rightPageID = tblAdClicks.projectPage)) AS totalViews
FROM tblAdClicks
WHERE projectID IN (SELECT projectID FROM tblProjects WHERE userID = 5)
GROUP BY projectID, urlID, projectPage
I need to order them so that all projects with ID = 111 come LAST in the returned data set.
So for example it might return:
Project ID
---------
100
100
100
156
156
143
122
111
111
111
190
154
87
But I need all the 111's to appear at the END of the list, without using two queries, one to select the 111's and the other to select the rest is not good enough unfortunately, as this is quite a resource intensive query.
ORDER BY
CASE projectID WHEN 111 THEN 1 ELSE 0 END,
projectID /* etc... */
add
order by cast (projectID as integer)